F    データ・リンク・インタフェース

データ・リンク・インタフェース (DLI) はプログラムがデータ・リンク機能を直接使用して,リモート・システムで実行されているデータ・リンク・プログラムとの通信を行なうためのプログラミング・インタフェースです。

クライアントおよびサーバの DLI プログラム例は F.5 項を参照してください。

F.1    DLI プログラミングの前提条件

DLI プログラミングには C 言語の知識とシステム・プログラムの経験が要求されます。 また,イーサネット・サブ構造体を使用する場合はイーサネット・プロトコルについての知識が,802 サブ構造体を使用する場合は 802.2,802.3 および FDDI プロトコルについての知識がそれぞれ必要です。

DLI インタフェースへのプログラムを書く場合はさらに次の概念についての知識が必要となります。

このオペレーティング・システムでの DLI アプリケーションの実行にはスーパユーザ特権またはルート特権が要求されます。

F.2    DLI 概略

DLI プログラムは標準イーサネット・フレーム・フォーマット,オープン・システム・インタコネクト (OSI) 802.3 フレーム・フォーマットまたは FDDI フレーム・フォーマットを使用してネットワーク上にデータを転送します。 オペレーティング・システムではインターネット・プログラム,DECnet プログラムおよび DLI プログラムを同時に実行することができます。

オペレーティング・システムはイーサネット・データ・リンク・サービスおよび 802.2 データ・リンク・サービスの両方をサポートします。 DLI および IP は両方ともイーサネットおよび 802.2 上で実行します。 FDDI と 802.3 は 802.2 論理リンク・コントロール (LLC) をデータ・リンクの補助層として使用します。 TCP および UDP は IP 上で実行し,両者を使用するプログラムにデータ配送サービスとメッセージ・ルーティング・サービスを提供します。 DLI はデータ・リンク層への直接アクセスを提供するので TCP および UDP が行なう高次レベル・サービスは提供しません。

図 F-1 は DLI と IP,DLI とイーサネットおよび DLI と 802.2 の関係を表した概念図です。

図 F-1:  DLI とネットワーク・プログラミング環境

ソケットは TCP,UDP および DLI へのアクセスを容易にするユーザ・アプリケーション・インタフェースです。 DLI 通信ドメイン (AF_DLI) でのソケットのオープンについては第 4 章を参照してください。

F.2.1    DLI サービス

DLI はデータ・リンク層で次のサービスを提供します。

F.2.2    ハードウェア・サポート

DLI は特定のシステムがどのドライバを構成しているかを probe ルーチンを使用して識別している,イーサネット・デバイス・ドライバまたは FDDI デバイス・ドライバを使用しますので,DLI ではハードウェアについての知識が必要とされません。 サポートされているネットワーク・デバイスの一覧は Tru64 UNIX の『ソフトウェア仕様書』に記載されています。

システムに構成されているネットワーク・デバイスは /usr/sbin/netstat -i コマンドを次のように使用して識別します。

% /usr/sbin/netstat -i
 
Name  Mtu   Network     Address      Ipkts Ierrs    Opkts Oerrs  Coll
ln0   1500  <Link>                     746     0      234     0    18
ln0   1500  orange-net  host1          746     0      234     0    18
sl0*  296   <Link>                       0     0        0     0     0
sl1*  296   <Link>                       0     0        0     0     0
lo0   1536  <Link>                      74     0       74     0     0
lo0   1536  loop        localhost       74     0       74     0     0
 

コマンドを入力すると,システムで構成されているインタフェースとデバイスの情報が画面に出力されます。 この例ではイーサネット・ハードウェア・デバイス (ln) が 2 つのシリアル・ライン・インタフェース・プロトコル・デバイス (sl0 および sl1) として構成されています。 sl0 および sl1 の後ろのアスタリスク (*) はインタフェースのサポートが有効になっていないことを示します。

F.2.3    DLI を使用してのローカル・エリア・ネットワークへのアクセス

単一のローカル・エリア・ネットワーク (LAN) コントローラ上のデータ・リンクは同時に複数のユーザをサポートします。 各局はネットワーク・チャネル上の使用可能なポートを表します。

ネットワーク・チャネルには同時に複数のユーザがアクセスするので,プログラムはメッセージを正しい受信者に確実に配送するアドレス機能を使用する必要があります。 ネットワークに転送するメッセージにはデスティネーション・システムを示すイーサネット・アドレスまたは FDDI アドレスが含まれている必要があります。 またメッセージがデスティネーション・システムの適切なユーザに届くために必要な識別子も含まれている必要があります (この識別子は使用するフレーム・フォーマットに依存します)。 DLI はイーサネット,IEEE 802.3 または FDDI 標準に従ってフレームを構築します。

F.2.4    高次レベル・サービスのインクルード

DLI はデータグラム・サービスのみを提供します。 DLI はデータ・リンク層への直接インタフェースなので,インターネットおよび DECnet では通常提供されている高次レベル・サービスを提供しません。 したがって,次のサービスをアプリケーションで提供する必要があります。

F.3    DLI ソケット・アドレスのデータ構造体

この節ではイーサネット,802.3 および FDDI の標準フレーム・フォーマットおよび DLI ソケット・アドレスのデータ構造体 (sockaddr_dl) の機能について記述します。 また,sockaddr_dl を使用してドメイン・アドレス,ネットワーク・デバイスおよびイーサネット,802.3 または FDDI サブ構造体を指定する方法を説明します。

F.3.1    標準フレーム・フォーマット

以下の 4 つの図はイーサネット,802.3 および FDDI フレームの相違点と類似点を示す概念図です。

図 F-2 はイーサネット・フレーム・フォーマットの概念図です。

図 F-2:  イーサネット・フレーム・フォーマット

図 F-3 は 802.3 フレーム・フォーマットの概念図です。 802.3 フレーム・フォーマットはその構造の中に図 F-5 の 802.2 構造を含んでいる点に注意してください。

図 F-3:  802.3 フレーム・フォーマット

図 F-4 は FDDI フレーム・フォーマットの概念図です。 FDDI フレーム・フォーマットもその構造の中に図 F-5 の 802.2 構造を含んでいます。

図 F-4:  FDDI フレーム・フォーマット

図 F-5 は 802.2 LLC PDU および 802.2 LLC SNAP PDU の概念図です。 これらのうちの 1 つが 802.3 および FDDI フレーム・フォーマットに含まれます。

図 F-5:  802.2 構造体

一般的に 802 アプリケーションは 802.2 LLC PDU フォーマットを使用します。 しかし,次のような理由から 802.2 LLC SNAP PDU フォーマットを選択することもできます。

F.3.2    sockaddr_dl 構造体の機能

DLI はデータ・リンク層の通信に必要とされるサービス群の構成に使用するソケット・アドレスのデータ構造体を提供します。 データ構造体 sockaddr_dl はアプリケーションがネットワークにバインドした時またはネットワークにパケットを転送した時に DLI へ情報を伝送するのに使用されます。 また,アプリケーションがネットワークからパケットを受信した時にアプリケーションに情報を伝送するのにも使用されます。 この情報にはネットワーク・デバイス情報,使用されるパケット・フォーマットおよびアドレス情報が含まれます。

次の例はヘッダ・ファイル <dli/dli_var.h> で定義されている DLI ソケット・アドレス構造体です。

#define DLI_ETHERNET    0
#define DLI_802         2

.
.
.
struct sockaddr_dl { u_char dli_len; /* length of sockaddr */ u_char dli_family; /* address family (AF_DLI) */ struct dli_devid dli_device; /* id of comm device to use */ u_char dli_substructype; /* id to interpret following */ /* structure */ union { struct sockaddr_edl dli_eaddr; /* Ethernet */ struct sockaddr_802 dli_802addr; /* OSI 802 support */ caddr_t dli_aligner1; /* this needs to have */ /* longword alignment */ } choose_addr; };  

すべてのアプリケーションはイーサネット・サブ構造体と 802 サブ構造体の両方を送受信することができます。 イーサネット・サブ構造体はアプリケーションのイーサネット間での通信を可能にし,802 サブ構造体はアプリケーションの 802.2,802.3 および FDDI プロトコル間での通信を可能にします。

イーサネット・サブ構造体または 802 サブ構造体を使用することで,ソケット・アドレス構造体の中でシステム・コールを使用して値を指定することができます。

サブ構造体のフィールドはシステム・コールの関数として更新されます。 たとえば bind システム・コールはドメイン,ネットワーク・デバイスおよびサブ構造体の指定に使用されます。 データの転送に sendto システム・コールを使用する場合はドメイン,ネットワーク・デバイスおよびサブ構造体の一部を指定する必要があります。 データの受信に recvfrom システム・コールを使用する場合,DLI は sockaddr 構造体のすべてのメンバに値を代入します。

ユーザが作成する dli_econn および dli_802_3_conn サブルーチンはソケットをオープンし,関連するドメイン,ネットワーク・デバイス名,プロトコル・タイプおよびその他のサブ構造体の情報をソケットにバインドします。 ユーザ作成のサブルーチン dli_econndli_802_3_conn の例は F.5 項を参照してください。

以下にイーサネット・サブ構造体と 802.2 サブ構造体が DLI sockaddr_dl データ構造体に提供する関数について説明します。

F.3.3    イーサネット・サブ構造体

以下は DLI イーサネット・ソケット・アドレス・サブ構造体の例です。

#define DLI_EADDRSIZE   6

.
.
.
struct sockaddr_edl { u_char dli_ioctlflg; /* i/o control flags */ u_char dli_options; /* Ethernet options */ u_short dli_protype; /* Ethernet protocol type */ u_char dli_target[DLI_EADDRSIZE]; /* Ethernet address of */ /* destination system */ u_char dli_dest[DLI_EADDRSIZE]; /* Ethernet address used to */ /* address the local system; */ }; /* DLI places the destination */ /* address of an incoming */ /* packet here to be used in */ /* the recvfrom call. This */ /* address can be the sys- */ /* tem's address or a multi */ /* cast address. */  

イーサネット・サブ構造体は次の項目を指定します。

この情報はイーサネット・フレーム・フォーマットの作成に使用されます。

F.3.3.1    イーサネット・フレーム

すべてのイーサネット・フレームはイーサネット・プロトコル・タイプ (PType) と呼ばれる 16 ビットの ID 番号を含みます。 コントローラにメッセージが届くと,フレームを受信したポートの識別にプロトコル・タイプが使用されます。 イーサネット間で通信する DLI アプリケーションは常に同じイーサネット・プロトコル・タイプを有効にしておく必要があります。 また,プロトコル・タイプを使用して着信パケットの宛先ユーザを選択する方法に加えて,DLI を構成して,リモート・システムのプロトコル・タイプおよび物理アドレスの両方の関数としてユーザを選択する方法があります。 これにより,同一システムの複数のアプリケーションが同一のタイプを使用することができるのでアプリケーションの出入力が簡素化されます。

F.3.3.2    イーサネット・サブ構造体の値の定義

ユーザは次のイーサネット・ソケット・アドレスサブ構造体のフィールドの値を定義します。 他方のフィールドはシステム・コールまたは DLI によって代入されます。

以下は,イーサネット・サブ構造体でユーザが定義することができるメンバの値の説明です。

デスティネーション・ノードの物理アドレス

デスティネーション・システムの物理アドレス (図 F-2 の DA) は製造元により各局に割り当てられるイーサネット上で固有の 48 ビットの値です。 物理アドレスは,たとえば 08-00-2b-XX-XX-XX (X は 16 進数) という形式で表わされます。 DA はローカル・システムから見たリモート・システムのアドレスです。

bind コールで DA 値を指定しない場合は sendto コールでデータの送信時に指定し,recvfrom コールでデータ・メッセージのソースを識別する必要があります。 sendto システム・コールへのメッセージの送信は物理アドレスまたはマルチキャスト・アドレスのどちらかを使用して行なうことができます。

プロトコル・タイプ

プロトコル・タイプ (図 F-2 の PType) はソース・アドレスに続くイーサネット・フレームの 16 ビットの値です。 イーサネット・ドライバはフレーム内のデータの受信者の決定に使用するプロトコル・タイプを DLI に渡します。 リザーブされている値を除いて,工場出荷時に割り当てられたイーサネット・プロトコル・タイプのうちシステム内で他に使用されていないものを任意に使用することができます。

次の 16 進数値はシステム用にリザーブされています。

I/O コントロール・フラグ

<dli/dli_var.h> ヘッダ・ファイルで定義されている I/O コントロール・フラグは DLI がプログラムのプロトコル・タイプのリザーブ方法の決定に使用する値です。 この値を使用して DLI はユーザをプロトコル・タイプに限った関数として選択するか,プロトコル・タイプとターゲット・オーディエンスの組み合わせの関数として選択するかを決定します。 次のリストは指定可能な I/O コントロール・フラグの定義と使用する際の条件の一覧です。

F.3.4    802.2 サブ構造体

802.2 サブ構造体によりアプリケーションは 802.2,802.3 および FDDI プロトコルを使用した相互通信が可能になります。 このサブ構造体はクラス I,タイプ 1 サービスとの 802.2 プロトコルを使用してアプリケーションされるサービスの 2 つのオペレーション・モードを使用します。

以下は DLI 802.3 ソケット・アドレスのサブ構造体の例です。

struct sockaddr_802 {               /* 802.3 sockaddr struct */
    u_char ioctl;                   /* filter on incoming packets */
                                    /* addressed to the SNAP SAP */
    u_char svc;                     /* service class for this portal */
    struct osi_802hdr eh_802;       /* OSI 802 header format */
};
 

802.2 サブ構造体は 802.3 フレーム・フォーマットと FDDI フレーム・フォーマットを包括します。 次のフィールドに値を指定することができます。

F.3.4.1    802 サブ構造体値の定義

以下は,802 サブ構造体で指定することができるすべてのメンバの値の説明です。

デスティネーション・ノードの物理アドレス

デスティネーション・システムの物理アドレス (DA) は製造元により各局に割り当てられるイーサネット上で固有の 48 ビットの値です。 物理アドレスは,たとえば 08-00-2b-XX-XX-XX (X は 16 進数) という形式で表されます。 このアドレスはアプリケーションがパケットの交換を試みるリモート・システムのアドレスです。 I/O コントロール・フィールドが EXCLUSIVE または DEFAULT で,サービス・アクセス・ポイント (SAP) が SNAP_SAP タイプである場合を除いて,このアドレスは bind コールで指定される必要があります。 また,SAP は sendto コールで指定される必要があります。

サービス・クラス

サービス・クラスはデータ・リンク層の論理リンク・コントロール (LLC) 補助層によって提供される機能を設定する 802.2 サブ構造体の値です。 指定可能なサービス・クラスは次のとおりです。

デスティネーション・サービス・アクセス・ポイント

デスティネーション・サービス・アクセス・ポイント (DSAP) はメッセージがどのアプリケーションに向けられているかを識別する 802.2 フレームのフィールドです。

1 人または 1 グループのユーザの識別に個人 DSAP またはグループ DSAP を使用することができます。 サービス・クラスが USER に設定されている場合に限りグループ DSAP を使用することができます。 このフィールドに指定することができる値は次のとおりです。

ソース・サービス・アクセス・ポイント

ソース・ サービス・アクセス・ポイント (SSAP) はメッセージを送信したアプリケーションのアドレスを識別する 802.2 フレームのフィールドです。 1 つのソケットで有効にできる SSAP は 1 つに限られます。 SSAP は 2 以上 254 以下の偶数です。

注意

SNAP_SAP を使用する場合は DSAP と SSAP の両方が SNAP_SAP に設定されている必要があります。 さらに,プロトコル識別子とコントロール・フィールドを指定しなければなりません。 プロトコル識別子は 5 バイト,コントロール・フィールドは 1 バイトです。 サービス・クラスが TYPE1 の場合に限り SNAP_SAP を有効にすることができます。

また,IEEE 802.2 標準ではすべての SAP アドレスの最下位から 2 番目のビットを 1 に設定して定義しています。 SAP の値は,目的にあわせて IEEE 802.2 標準に定義に従って使用することをお勧めします。

コントロール・フィールド

コントロール・フィールドはパケット・タイプを指定します。 次の値がクラス I,タイプ 1 サービスに定義されています。 以下に記述されている値はクラス I,タイプ 1 サービス用に定義されたものです。 これらはクラス II,タイプ 2 サービスを提供するためにユーザ・サプライ・モードで使用することもできます。

注意

このユーザ・モードを使用するアプリケーションは適切なサービスを提供する役割を担います。 クラス II サービスでサポートされるその他のオペレーションについては,米国電気電子技術者協会 (IEEE) によって発行されている『IEEE Standards for Local Area Networks: Logical Link Control』を参照してください。

F.4    DLI プログラムの作成

この節では,DLI プログラムを書く際のシステム・コールの使用方法,およびイーサネット・サブ構造体と 802 サブ構造体内で値を指定する際の手順について説明します。

F.5 項にこの節で説明する手順の DLI プログラミング例が記載されています。

アプリケーション・プログラムを書く際のソケットおよびシステム・コールの使用方法の詳細は第 4 章を参照してください。

F.4.1    データ・リンク・サービスの提供

DLI はデータグラム・サービスのみを提供するので,DLI アプリケーションは高次レベルのネットワーク・ソフトウェアが通常提供するサービスを提供する必要があります。

F.4.2    Tru64 UNIX システム・コール

DLI プログラムは入力引数,構造体および DLI に特化したサブ構造体とともにソケット・インタフェースを使用します。 たとえば socket システム・コールを発行するときにプログラムはアドレス・フォーマット AF_DLI およびプロトコル DLPROTO_DLI を使用します。

DLI プログラムの先頭で <dli/dli_var.h>. ヘッダ・ファイルをインクルードする必要があります。 プログラムの本文は表 F-1 の呼び出し順序に従って記述します。

表 F-1:  DLI プログラムの呼び出し順序

機能 システム・コール
ソケットの作成 socket
アドレス・ファミリ,フレーム・フォーマットおよび sockaddr_dl 構造体を使用してプログラムがデータを送信するデバイスを指定してソケットとデバイスをバインド bind
ソケット・オプションの設定。 このコールはオプションです。 setsockopt
データの転送 sendrecvreadwritesendtorecvfrom
ソケット・デスクリプタの非アクティブ化 close

各システム・コールの詳細は第 4 章およびリファレンス・ページを参照してください。

以降の各項では DLI 関数,入力引数および構造体について説明します。

F.4.3    ソケットの作成

DLI アプリケーションでは socket システム・コールに次の入力引数を指定してソケットを作成する必要があります。

アドレス・ファミリ: AF_DLI
ソケット・タイプ: SOCK_DGRAM
プロトコル: DLPROTO_DLI

AF_DLI は DLI アドレス・ファミリを指定します。 SOCK_DGRAM は DLI で使用できる唯一のソケット・タイプのデータグラム・ソケットを作成します。 DLI は他のプログラムに接続するために必要なサービスおよび他のソケット・タイプを使用するために必要なサービスを提供していません。 値 DLPROTO_DLI は DLI プロトコル・モジュールを指定します。

以下は DLI にソケットをオープンする際の socket コールの使用例です。

int so;

.
.
.
if ( (so = socket(AF_DLI,SOCK_DGRAM,DLPROTO_DLI))<0) { perror("cannot open DLI socket"); return (-1); }  

F.4.4    ソケット・オプションの設定

setsockopt コールを使用して,sockaddr_dl 構造体に次のソケット・オプションを設定します。

オプション 説明
DLI_ENAGSAP グループ・サービス・アクセス・ポイント (DSAP) を使用可能にする。
DLI_DISGSAP グループ・サービス・アクセス・ポイント(DSAP)を使用不可にする。
DLI_SET802CTL 802 コントロール・フィールドを設定する。
DLI_MULTICAST マルチキャスト・アドレスにアドレスされたすべてのメッセージの受信を可能にする。

以下のコードはソケット・オプションを設定する際の setsockopt コールの例です。

この例は setsockopt コールを使用して GSAP オプションを使用可能にするものです。

/* enable GSAPs supplied by user */
 j = 3;
 i = 0;
 while (j < argc ) {
     sscanf(argv[j++], "%x", &k);
     out_opt[i++] = k;
 }
 optlen = i;
 if
 (setsockopt(sock,DLPROTO_DLI,DLI_ENAGSAP,&out_opt[0],optlen) < 0){
     perror("dli_setsockopt: Can't enable gsap");
     exit(1);
 }
 

この例は setsockopt コールを使用して GSAP オプションを使用不可にするものです。

 /* disable all but the last 4 or all GSAPs, */
 /* whichever is smallest */
 if ( optlen > 4 )
     optlen -= 4;
 if
 (setsockopt(sock,DLPROTO_DLI,DLI_DISGSAP,&out_opt[0],optlen) < 0){
     perror("dli_setsockopt: Can't disable gsap");
 }
 

この例は setsockopt コールを使用して 802 コントロール・フィールドを設定するものです。

 /* set 802 control field */
 out_opt[0] = TEST_PCMD;
 optlen = 1;
 if
 (setsockopt(sock,DLPROTO_DLI,DLI_SET802CTL,
             &out_opt[0],optlen)<0){
     perror("dli_setsockopt: Can't set 802 control");
     exit(1);
 }
 

この例は setsockopt コールを使用して 2 つのマルチキャスト・アドレスを使用可能にするものです。

 /* enable two multicast addresses */
 bcopy(mcast0, out_opt, sizeof(mcast0));
 bcopy(mcast1, out_opt+sizeof(mcast0), sizeof(mcast1));
  if ( setsockopt(sock, DLPROTO_DLI, DLI_MULTICAST, &out_opt[0],
                 (sizeof(mcast0) + sizeof(mcast1))) < 0 ) {
     perror("dli_setsockopt: can't enable multicast");
 }
 

詳細な例についてはF.5 項を参照してください。

F.4.5    ソケットのバインド

ソケットの作成後,アプリケーションはソケットとネットワーク・デバイスをバインドしなければなりません。 この時点でメッセージのフォーマット・タイプを指定します。 まずソケットに名前を割り当てます。 変数 namesockaddr_dl タイプの構造体のポインタとなります。 次に,sockaddr_dl データ構造体に情報を代入し適当なサブ構造体 (イーサネットまたは 802) をインクルードします。

ソケットのバインドには次の bind システム・コールを使用します。 詳細は bind(2) を参照してください。

bind システム・コールについての詳細はリファレンス・ページ bind(2) を参照してください。

F.4.6    sockaddr_dl 構造体

sockaddr_dl 構造体には次の情報を代入します。

F.4.6.1    アドレス・ファミリの指定

アドレス・ファミリの指定には,socket コール内で値 AF_DLI を使用します。

F.4.6.2    I/O デバイス ID の指定

I/O デバイスはプログラムがどのターゲット・システムを送信先および送信元とするかを制御するコントローラです。 I/O デバイス ID はデバイス名 dli_devname およびデバイス番号 dli_devnumber を含みます。 各変数の定義は次の情報をもとに行ないます。

F.4.6.3    サブ構造体タイプの指定

サブ構造体はプログラムが使用するフレーム・フォーマットのタイプを指定します。

各タイプに関連するソケットがある場合,プログラムはイーサネット,802.3 および FDDI フレームを送受信することができます。 たとえば,あるシステムではイーサネット・フレームを使用して通信を行ない,他のシステムでは 802.3 または FDDI フレームを使用している場合があります。 フレーム・フォーマットの選択はターゲット・プログラムが使用しているフレームのタイプに依存します。 しかし,1 つのソケットが対応するフレームのタイプは 1 つに限られます。

プログラムはサブ構造体にユーザの選択を代入することでメッセージの送信用のパケット・ヘッダを指定します。 例 F-1 はイーサネット・プロトコル用に sockaddr_dl 構造体に値を代入する例です。 例 F-2 は 802 プロトコル用に sockaddr_dl 構造体に値を代入する例です。

例 F-1:  イーサネット用 sockaddr_dl 構造体の代入

 /*
  * Fill out the sockaddr_dl structure for the bind call
  */
 bzero(&out_bind, sizeof(out_bind));
 out_bind.dli_family = AF_DLI;
 out_bind.dli_substructype = DLI_ETHERNET;
 bcopy(devname, out_bind.dli_device.dli_devname, i);
 out_bind.dli_device.dli_devnumber = devunit;
 out_bind.choose_addr.dli_eaddr.dli_ioctlflg = ioctl;
 out_bind.choose_addr.dli_eaddr.dli_protype = ptype;
 if ( taddr )
      bcopy(taddr, out_bind.choose_addr.dli_eaddr.dli_target,
            DLI_EADDRSIZE);
 
  if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
 {
      perror("dli_eth, can't bind DLI socket");
      return(-1);
 }
 
  return(sock);
}
 

例 F-2:  802.2 用 sockaddr_dl 構造体の代入

 /*
  * Fill out sockaddr_dl structure for the bind call.
  * Note that we need to determine whether the
  * control field is 8 bits (unnumbered format) or
  * 16 bits (informational/supervisory format).  We do this
  * by checking the low order 2 bits, which are both 1 only
  * for unnumbered control fields.
  */
 bzero(&out_bind, sizeof(out_bind));
 out_bind.dli_family = AF_DLI;
 out_bind.dli_substructype = DLI_802;
 bcopy(devname, out_bind.dli_device.dli_devname, i);
 out_bind.dli_device.dli_devnumber = devunit;
 out_bind.choose_addr.dli_802addr.ioctl = ioctl;
 out_bind.choose_addr.dli_802addr.svc = svc;
 if(ctl & 3)
     out_bind.choose_addr.dli_802addr.eh_802.ctl.U_fmt=(u_char)ctl;
 else
     out_bind.choose_addr.dli_802addr.eh_802.ctl.I_S_fmt = ctl;
 out_bind.choose_addr.dli_802addr.eh_802.ssap = sap;
 out_bind.choose_addr.dli_802addr.eh_802.dsap = dsap;
 if ( ptype )
     bcopy(ptype,out_bind.choose_addr.dli_802addr.eh_802.osi_pi,5);
 if ( taddr )
      bcopy(taddr, out_bind.choose_addr.dli_802addr.eh_802.dst,
            DLI_EADDRSIZE);
 if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
 {
      perror("dli_802, can't bind DLI socket");
      return(-1);
 }
 
  return(sock);
}

F.4.7    バッファ・サイズの計算

バッファ・サイズは通信しているシステムのコントローラが処理できるサイズを超えてはなりません。 超えた場合データは失われます。 イーサネット・パケットのバッファ・サイズの最大値は 1500 バイトです。

802.3 パケットのバッファ・サイズの最大値は次の式で算出します。

    bytes = 1500 - [ 2 + (control field == UI? 1:2) +
                    (Source SAP == SNAP SAP ? 5:0)]
 

コントロール・フィールドおよび Source SAP のバイト数は bind コールに指定されています。

FDDI パケットのバッファ・サイズの最大値は 4352 バイトです。

F.4.8    データ転送

DLI プログラムは writesend または sendto コールを使用してデータを送信,readrecv または recvfrom コールを使用してデータを受信することができます。 表 F-2 の "○" は bind コール中に設定される I/O コントロール・フラグの関数として使用することができるシステム・コールの条件を示します。

注意

Normal コントロール・フラグを使用する場合は bind コールでターゲット・アドレスを設定する必要があります。 Exclusive または Default コントロール・フラグを使用する場合は bind コールでターゲット・アドレスを指定する必要はありません。 しかし,ターゲット・アドレスを設定しない場合は sendto および recvfrom システム・コールを使用する必要があります。

表 F-2:  DLI で使用されるデータ転送システム・コール

システム・コール Normalコントロール Exclusiveコントロール Defaultコントロール
write    
send    
sendto
read    
recv    
recvfrom

コントロール・フラグを NORMAL に設定した場合は bind コールでターゲット・アドレスを設定して,writesendsendtoreadrecvrecvfrom コールを使用してデータを転送します。

コントロール・フラグを EXCLUSIVE に設定した場合は bind コールのターゲット・アドレスの値をゼロにして,sendto コールでターゲット・アドレスを設定します。 データ転送には sendto および recvfrom コールだけを使用します。

コントロール・フラグを DEFAULT に設定した場合は bind コールのターゲット・アドレスの値をゼロにして,sendto コール使用してデータの送信を行ない,このコール内でターゲット・アドレスを設定します。 データのソース・アドレスの識別には recvfrom コールを使用します。

F.4.9    ソケットの非アクティブ化

データの送受信が終了したら,close システム・コールを発行してソケットを非アクティブ化します。

F.5    DLI プログラミング例

この節では次の DLI プログラミング例を記述します。

上記のプログラム例は /usr/examples/dli ディレクトリにあります。

F.5.1    イーサネット・フォーマット・パケットを使用した DLI クライアント・プログラム例

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <memory.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/route.h>
#include <dli/dli_var.h>
 
/*
 *      d l i _ e x a m p l e : d l i _ e t h
 *
 * Description: This program sends out a message to a node where a
 *              companion program, dli_ethd, echoes the message back.
 *              The ethernet packet format is used.  The ethernet
 *              address of the node where the companion program is
 *              running, the protocol type, and the message are
 *              supplied by the user.  The companion program should
 *              be started before executing this program.
 *
 * Inputs:      device, target address, protocol type, short message.
 *
 * Outputs:     Exit status.
 *
 * To compile:  cc -o dli_eth dli_eth.c
 *
 * Example:     dli_eth ln0 08-00-2b-02-e2-ff 6006 "Echo this"
 *
 * Comments:    This example demonstrates the use of the "NORMAL" I/O
 *              control flag.  The use of the "NORMAL" flag means that
 *              we can communicate only with a single specific node
 *              whose address is specified during the bind.  Because
 *              of this, we can use the normal write & read system
 *              calls on the socket, because the source/destination of
 *              all data that is read/written on the socket is fixed.
 *
 */
 
/*
 * Compaq Computer Corporation supplies this software example on
 * an "as-is" basis for general customer use.  Note that Compaq
 * does not offer any support for it, nor is it covered under any
 * of Compaq's support contracts.
 */
 
main(
    int argc,
    char **argv)
{
    struct sockaddr_dl sdl;
    size_t sdllen;
    int ch, fd, rsize, itarget[6], ptype, ioctlflg = DLI_NORMAL, errflg = 0;
    u_char inbuf[4800], u_char *src;
 
    memset(&sdl, 0, sizeof(sdl));
    while ((ch = getopt(argc, argv, "xp:")) != EOF) {
	    case 'x': ioctlflg = DLI_EXCLUSIVE; break;
	    case 'p': {
	        if (sscanf(optarg, "%x", &ptype, &ch) != 1) {
            fprintf(stderr, "%s: invalid protocol type "s
			            argv[0], optarg);
		        errflg++;
		        break;
	        }
	    }
	    default:	errflg++; break;
    }
 
    if (errflg || argc - optind < 5) {
        fprintf(stderr, "%s %s %s\n",
		"usage:",
		argv[0],
		"device lan-address short-message");
        exit(1);
    }
 
    /*
     * Get device name and unit number.
     */
    if (sscanf(argv[optind], "%[a-z]%hd%c", sdl.dli_device.dli_devname,
	   &sdl.dli_device.dli_devnumber, &ch) != 2) {
	fprintf(stderr, "%s: invalid device name
        argv[0], argv[optind]);
	exit(1);
    }
 
    /*
     * Get the address to which we will be sending
     */
    if (sscanf(argv[++optind], "%x%*[:-]%x%*[:-]%x%*[:-]\
                                %x%*[:-]%x%*[:-]%x%c",
	   &itarget[0], &itarget[1], &itarget[2],
	   &itarget[3], &itarget[4], &itarget[5], &ch) != 6) {
	fprintf(stderr, "%s: invalid lan address
        argv[0], argv[optind]);
	exit(1);
    }
 
    /*
     *	If the LAN Address is a multicast, then we can't
     *  use DLI_NORMAL.  Use DLI_DEFAULT instead.
     */
    if ((itarget[0] & 1) && ioctflg == DLI_NORMAL)
	    ioctlflg = DLI_DEFAULT;
 
    /*
     * Fill out sockaddr structure for bind/sento/recvfrom
     */
    sdl.dli_family = AF_DLI;
    if (ptype < GLOBAL_SAP) {
	    sdl.dli_substructype = DLI_802;
	    sdl.choose_addr.dli_802addr.ioctl = ioctlflg;
	    sdl.choose_addr.dli_802addr.svc = TYPE1;
	    sdl.choose_addr.dli_802addr.eh_802.dsap = ptype;
	    sdl.choose_addr.dli_802addr.eh_802.ssap = ptype;
	    sdl.choose_addr.dli_802addr.eh_802.ctl.U_fmt = UI_NPCMD;
	    src = sdl.choose_addr.dli_802addr.eh_802.dst;
    } else {
	    sdl.dli_substructype = DLI_ETHERNET;
	    sdl.choose_addr.dli_eaddr.dli_ioctlflg = ioctlflg;
	    sdl.choose_addr.dli_eaddr.dli_protype = ptype;
	    src = sdl.choose_addr.dli_eaddr.dli_target;
    }
    /*
     * If we are using DLI_NORMAL, we must supply
     */
    if (ioctlflg == DLI_NORMAL) {
	    src[0] = itarget[0]; src[1] = itarget[1]; src[2] = itarget[2];
	    src[3] = itarget[3]; src[4] = itarget[4]; src[5] = itarget[5];
    }
 
    /*
     * Open a socket to DLI and then bind to our protocol/address.
     */
    if ((fd = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0) {
	    fprintf(stderr, "%s: DLI open failed: %s\n",
		        argv[0], strerror(errno));
	    exit(1);
    }
 
    if (bind(fd, (struct sockaddr *) &sdl, sizeof(sdl)) < 0) {
	    fprintf(stderr, "%s: DLI bind failed: %s\n",
		        argv[0], strerror(errno));
	    exit(2);
    }
 
    if (ioctlflg != DLI_NORMAL) {
	    src[0] = itarget[0]; src[1] = itarget[1]; src[2] = itarget[2];
	    src[3] = itarget[3]; src[4] = itarget[4]; src[5] = itarget[5];
    }
 
    /* send response to originator. */
    sdllen = sizeof(sdl);
    if (sendto(fd, argv[4], strlen(argv[4]), 0,
	       (struct sockaddr *) &sdl, sdllen) < 0) {
	    fprintf(stderr, "%s: DLI transmission failed: %s\n",
		        argv[0], strerror(errno));
	    exit(1);
    }
 
    if ((rsize = recvfrom(fd, inbuf, sizeof(inbuf), 0,
			             (struct sockaddr *) &sdl, &sdllen)) < 0 ) {
	    fprintf(stderr, "%s: DLI reception failed: %s\n",
		        argv[0], strerror(errno));
	  exit(1);
    }
 
    /* check header */
    if (sdllen != sizeof(struct sockaddr_dl)) {
	    fprintf(stderr, "%s, incorrect header supplied\n", argv[0]);
	    exit(1);
    }
 
    if (from.dli_substructype == DLI_802)
	    src = from.dli_choose_addr.dli_802addr.eh_802.dst;
    else
	    src = from.dli_choose_addr.dli_eaddr.dli_target;
 
    /* any data? */
    fprintf(stderr, "%s: %sdata received from ", argv[0],
            rsize ?  : "NO ");
    fprintf(stderr, "%02x-%02x-%02x-%02x-%02x-%02x",
	        src[0], src[1], src[2], src[3], src[4], src[5]);
    if (from.dli_substructype == DLI_802)
	    fprintf(stderr, " SAP %02x\n\n",
		        sdl.choose_addr.dli_802addr.eh_802.ssap & ~1);
    else
	    fprintf(stderr, " on protocol type %04x\n\n",
		        sdl.choose_addr.dli_eaddr.dli_protype);
 
    /* print results */
    printf("%s\n", inbuf);
    close(fd);
    return 0;
}
 

F.5.2    イーサネット・フォーマット・パケットを使用した DLI サーバ・プログラム例

#ifndef lint
static char *rcsid = "@(#)$RCSfile: ap-dli.sgml,v $ \
         $Revision: 1.1.6.3 $ (DEC) $Date: 1999/07/08 20:46:48 $";
#endif
 
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dli/dli_var.h>
#include <sys/ioctl.h>
 
extern int errno;
 
/*
 *      d l i _ e x a m p l e : d l i _ e t h d
 *
 * Description: This daemon program transmits any message it
 *  receives to the originating system, i.e., it echoes the
 *  message back.  The device and protocol type are supplied
 *  by the user.  The program uses ethernet format packets.
 *
 * Inputs:      device, protocol type.
 *
 * Outputs:     Exit status.
 *
 * To compile:  cc -o dli_ethd dli_ethd.c
 *
 * Example:     dli_ethd de0 6006
 *
 * Comments:    This example demonstrates the use of the "DEFAULT"
 *  I/O control flag, and the recvfrom & sendto system calls.
 *  By specifying "DEFAULT" when binding the DLI socket to
 *  the device we inform the system that this program will
 *  receive any ethernet format packet with the given
 *  protocol type which is not meant for any other program
 *  on the system.  Since packets may arrive from
 *  different systems we use the recvfrom call to read the
 *  packets.  This call gives us access to the packet
 *  header information so that we can determine where the
 *  packet came from.  When we write on the socket we must
 *  use the sendto system call to explicitly give the
 *  destination of the packet.
 */
 
/*
 * Compaq Computer Corporation supplies this software
 * example on an "as-is" basis for general customer use.  Note
 * that Compaq does not offer any support for it, nor is it
 * covered under any of Compaq's support contracts.
 */
 
main(argc, argv, envp)
int argc;
char **argv, **envp;
 
{
 
    u_char inbuf[1500], outbuf[1500];
    u_char devname[16];
    u_char target_eaddr[6];
    char *cp;
    int rsize;
    unsigned int devunit;
    int i, sock, fromlen;
    unsigned int ptype;
    struct sockaddr_dl from;
 
    if ( argc < 3 )
    {
        fprintf(stderr,
                "usage: %s device hex-protocol-type\n", argv[0]);
        exit(1);
    }
 
    /* get device name and unit number. */
    bzero(devname, sizeof(devname));
    i = 0;
    cp = argv[1];
    while ( isalpha(*cp) )
        devname[i++] = *cp++;
    sscanf(cp, "%d", &devunit);
 
    /* get protocol type */
    sscanf(argv[2], "%x", &ptype);
 
    /* open dli socket */
    if
    ((sock = dli_econn(devname, devunit, ptype, NULL, \
             DLI_DEFAULT))<0)
    {
        perror("dli_ethd, dli_econn failed");
        exit(1);
    }
 
    while ( 1 ) {
        /* wait for message */
        from.dli_family = AF_DLI;
        fromlen = sizeof(struct sockaddr_dl);
        if ((rsize = recvfrom(sock, inbuf, sizeof(inbuf),
                              NULL, &from, &fromlen)) < 0 ) {
            sprintf(inbuf, "%s: DLI reception failed", argv[0]);
            perror(inbuf);
            exit(2);
        }
 
        /* check header */
        if ( fromlen != sizeof(struct sockaddr_dl) ) {
            fprintf(stderr,"%s, incorrect header supplied\n",argv[0]);
            continue;
        }
 
        /* any data? */
        if ( ! rsize )
            fprintf(stderr, "%s, NO data received from ", argv[0]);
        else
            fprintf(stderr, "%s, data received from ", argv[0]);
        for ( i = 0; i < 6; i++ )
            fprintf(stderr, "%x%s",
                    from.choose_addr.dli_eaddr.dli_target[i],
                    ((i<5)?"-":" "));
        fprintf(stderr, "on protocol type %x\n",
                from.choose_addr.dli_eaddr.dli_protype);
 
        /* send response to originator. */
        if ( sendto(sock, inbuf, rsize, NULL, &from, fromlen) < 0 ) {
            sprintf(outbuf, "%s: DLI transmission failed", argv[0]);
            perror(outbuf);
            exit(2);
        }
    }
}
 
/*
 *              d l i _ e c o n n
 *
 *
 *
 * Description:
 *      This subroutine opens a dli socket, then binds an associated
 *      device name and protocol type to the socket.
 *
 * Inputs:
 *      devname         = ptr to device name
 *      devunit         = device unit number
 *      ptype           = protocol type
 *      taddr           = target address
 *      ioctl           = io control flag
 *
 * Outputs:
 *      returns         = socket handle if success, otherwise -1
 */
 
dli_econn(devname, devunit, ptype, taddr, ioctl)
char *devname;
unsigned devunit;
unsigned ptype;
u_char *taddr;
u_char ioctl;
{
    int i, sock;
    struct sockaddr_dl out_bind;
 
    if ( (i = strlen(devname)) >
          sizeof(out_bind.dli_device.dli_devname) )
    {
         fprintf(stderr, "dli_ethd: bad device name");
         return(-1);
    }
 
    if ((sock = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0)
    {
         perror("dli_ethd, can't open DLI socket");
         return(-1);
    }
    /*   Fill out bind structure     */
 
    bzero(&out_bind, sizeof(out_bind));
    out_bind.dli_family = AF_DLI;
    out_bind.dli_substructype = DLI_ETHERNET;
    bcopy(devname, out_bind.dli_device.dli_devname, i);
    out_bind.dli_device.dli_devnumber = devunit;
    out_bind.choose_addr.dli_eaddr.dli_ioctlflg = ioctl;
    out_bind.choose_addr.dli_eaddr.dli_protype = ptype;
    if ( taddr )
         bcopy(taddr, out_bind.choose_addr.dli_eaddr.dli_target,
               DLI_EADDRSIZE);
 
    if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
    {
         perror("dli_ethd, can't bind DLI socket");
         return(-1);
    }
 
    return(sock);
}
 
 

F.5.3    802.3 フォーマット・パケットを使用した DLI クライアント・プログラム例

#ifndef lint
static  char  *sccsid = "@(#)dli_802.c	1.1	(DEC OSF/1)	5/29/92";
#endif lint
 
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dli/dli_var.h>
#include <sys/ioctl.h>
 
extern int errno;
 
#define PROTOCOL_ID      {0x00, 0x00, 0x00, 0x00, 0x5}
u_char protocolid[] = PROTOCOL_ID;
 
/*
 *      d l i _ e x a m p l e : d l i _ 8 0 2
 *
 * Description: This program sends out a message to a system
 *    where a companion program, dli_802d, echoes the message
 *    back.  The 802.3 packet format is used.  The ethernet
 *    address of the system where the companion program is
 *    running, the sap, and the message are supplied by the
 *    user.  The companion program should be started before
 *    executing this program.
 *
 * Inputs:      device, target address, sap, short message.
 *
 * Outputs:     Exit status.
 *
 */
#ifndef lint
static char *rcsid = "@(#)$RCSfile: ap-dli.sgml,v $ \
            $Revision: 1.1.6.3 $ (DEC) $Date: 1999/07/08 20:46:48 $";
#endif
 
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dli/dli_var.h>
#include <sys/ioctl.h>
 
extern int errno;
 
#define PROTOCOL_ID      {0x00, 0x00, 0x00, 0x00, 0x5}
u_char protocolid[] = PROTOCOL_ID;
 
/*
 *      d l i _ e x a m p l e : d l i _ 8 0 2
 *
 * Description: This program sends out a message to a system
 *    where a companion program, dli_802d, echoes the message
 *    back.  The 802.3 packet format is used.  The ethernet
 *    address of the system where the companion program is
 *    running, the sap, and the message are supplied by the
 *    user.  The companion program should be started before
 *    executing this program.
 *
 * Inputs:      device, target address, sap, short message.
 *
 * Outputs:     Exit status.
 *
 * To compile:  cc -o dli_802 dli_802.c
 *
 * Example:     dli_802 qe0 08-00-2b-02-e2-ff ac "Echo this"
 *
 * Comments:    This example demonstrates the use of 802 "TYPE1"
 *    service.  With TYPE1 service, the processing of
 *    XID and TEST messages is handled transparently by
 *    DLI, i.e., this program doesn't have to be concerned
 *    with handling them.  If the SNAP SAP (0xAA) is
 *    selected, a 5 byte protocol id is also required.
 *    This example automatically uses a protocol id of
 *    of PROTOCOL_ID when the SNAP SAP is used.  Also,
 *    note the use of DLI_NORMAL for the i/o control flag.
 *    DLI makes use of this only when that SNAP_SAP/Protocol
 *    ID pair is used. DLI will filter all incoming messages
 *    by comparing the Ethernet source address and Protocol
 *    ID against the target address and Protocol ID set up
 *    in the bind call.  Only if a match occurs will DLI
 *    pass the message up to the application.
 */
 
/*
 * Compaq Computer Corporation supplies this software
 * example on an "as-is" basis for general customer use.  Note
 * that Compaq does not offer any support for it, nor is it
 * covered under any of Compaq's support contracts.
 */
 
main(argc, argv, envp)
int argc;
char **argv, **envp;
 
{
 
    u_char inbuf[1500], outbuf[1500];
    u_char target_eaddr[6];
    u_char devname[16];
    int rsize, devunit;
    char *cp;
    int i, sock, fromlen;
    struct sockaddr_dl from;
    unsigned int obsiz, byteval;
    u_int sap;
    u_char  *pi = 0;
 
    if ( argc < 5 )
    {
        fprintf(stderr, "%s %s %s\n",
                "usage:",
                argv[0],
                "device ethernet-address hex-sap short-message");
        exit(1);
    }
 
    /* get device name and unit number. */
    bzero(devname, sizeof(devname));
    i = 0;
    cp = argv[1];
    while ( isalpha(*cp) )
        devname[i++] = *cp++;
    sscanf(cp, "%d", &devunit);
 
    /* get phys addr of remote system */
    bzero(target_eaddr, sizeof(target_eaddr));
    i = 0;
    cp = argv[2];
    while ( *cp ) {
        if ( *cp == '-' ) {
            cp++;
            continue;
        }
        else {
            sscanf(cp, "%2x", &byteval );
	    target_eaddr[i++] = byteval;
            cp += 2;
        }
    }
 
    /* get sap */
    sscanf(argv[3], "%x", &sap);
 
    /* get message */
    bzero(outbuf, sizeof(outbuf));
    if ( (obsiz = strlen(argv[4])) > 1500 ) {
        fprintf(stderr, "%s: message is too long\n", argv[0]);
        exit(2);
    }
    strcpy(outbuf, argv[4]);
 
    /* open dli socket. notice that if (and only if) the */
    /* snap sap was selected then a protocol id must also */
    /* be provided. */
    if ( sap == SNAP_SAP )
        pi = protocolid;
    if ( (sock = dli_802_3_conn(devname, devunit, pi, target_eaddr,
                  DLI_NORMAL, TYPE1, sap, sap, UI_NPCMD)) < 0 ) {
        perror("dli_802, dli_econn failed");
        exit(3);
    }
 
    /* send message to target. minimum message size is 46 bytes. */
    if ( write(sock, outbuf, (obsiz < 46 ? 46 : obsiz)) < 0 ) {
        sprintf(outbuf, "%s: DLI transmission failed", argv[0]);
        perror(outbuf);
        exit(4);
    }
 
    /* wait for response from correct address */
    while (1) {
        bzero(&from, sizeof(from));
        from.dli_family = AF_DLI;
        fromlen = sizeof(struct sockaddr_dl);
        if ((rsize = recvfrom(sock, inbuf, sizeof(inbuf),
                              NULL, &from, &fromlen)) < 0 ) {
              sprintf(inbuf, "%s: DLI reception failed", argv[0]);
              perror(inbuf);
              exit(5);
        }
        if ( fromlen != sizeof(struct sockaddr_dl) ) {
              fprintf(stderr,"%s, invalid address size\n",argv[0]);
              exit(6);
        }
        if ( bcmp(from.choose_addr.dli_802addr.eh_802.dst,
                  target_eaddr, sizeof(target_eaddr)) == 0 )
                break;
    }
 
    if ( ! rsize ) {
        fprintf(stderr, "%s, no data returned\n", argv[0]);
        exit(7);
    }
    /* print message */
    printf("%s\n", inbuf);
 
    close(sock);
 
}
 
/*
 *              d l i _8 0 2 _ 3 _ c o n n
 *
 *
 *
 * Description:
 *      This subroutine opens a dli 802.3 socket, then binds an
 *      associated device name and protocol type to the socket.
 *
 * Inputs:
 *      devname         = ptr to device name
 *      devunit         = device unit number
 *      ptype           = protocol type
 *      taddr           = target address
 *      ioctl           = io control flag
 *      svc             = service class
 *      sap             = source sap
 *      dsap            = destination sap
 *      ctl             = control field
 *
 *
 * Outputs:
 *      returns         = socket handle if success, otherwise -1
 *
 *
 */
 
dli_802_3_conn (devname,devunit,ptype,taddr,ioctl,svc,sap,dsap,ctl)
char *devname;
u_short devunit;
u_char *ptype;
u_char *taddr;
u_char ioctl;
u_char svc;
u_char sap;
u_char dsap;
u_short ctl;
 
{
    int i, sock;
    struct sockaddr_dl out_bind;
 
    if ( (i = strlen(devname)) >
         sizeof(out_bind.dli_device.dli_devname) )
    {
         fprintf(stderr, "dli_802: bad device name");
         return(-1);
    }
 
    if ((sock = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0)
    {
         perror("dli_802, can't open DLI socket");
         return(-1);
    }
 
    /*
     * Fill out bind structure.  Note that we need to determine
     * whether the ctl field is 8 bits (unnumbered format) or
     * 16 bits (informational/supervisory format).  We do this
     * by checking the low order 2 bits, which are both 1 only
     * for unnumbered control fields.
     */
    bzero(&out_bind, sizeof(out_bind));
    out_bind.dli_family = AF_DLI;
    out_bind.dli_substructype = DLI_802;
    bcopy(devname, out_bind.dli_device.dli_devname, i);
    out_bind.dli_device.dli_devnumber = devunit;
    out_bind.choose_addr.dli_802addr.ioctl = ioctl;
    out_bind.choose_addr.dli_802addr.svc = svc;
    if(ctl & 3)
        out_bind.choose_addr.dli_802addr.eh_802.ctl.U_fmt=\
              (u_char)ctl;
    else
        out_bind.choose_addr.dli_802addr.eh_802.ctl.I_S_fmt = \
              ctl;
    out_bind.choose_addr.dli_802addr.eh_802.ssap = sap;
    out_bind.choose_addr.dli_802addr.eh_802.dsap = dsap;
    if ( ptype )
        bcopy(ptype,out_bind.choose_addr.dli_802addr.eh_802.osi_pi,\
              5);
    if ( taddr )
         bcopy(taddr, out_bind.choose_addr.dli_802addr.eh_802.dst,
               DLI_EADDRSIZE);
    if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
    {
         perror("dli_802, can't bind DLI socket");
         return(-1);
    }
 
    return(sock);
}
 
 

F.5.4    802.3 フォーマット・パケットを使用した DLI サーバ・プログラム例

#ifndef lint
static char *rcsid = "@(#)$RCSfile: ap-dli.sgml,v $ \
            $Revision: 1.1.6.3 $ (DEC) $Date: 1999/07/08 20:46:48 $";
#endif
 
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dli/dli_var.h>
#include <sys/ioctl.h>
 
extern int errno;
 
#define PROTOCOL_ID      {0x00, 0x00, 0x00, 0x00, 0x5}
u_char protocolid[] = PROTOCOL_ID;
 
/*
 *      d l i _ e x a m p l e : d l i _ 8 0 2 d
 *
 * Description: This daemon program transmits any message it
 *   receives to the originating system, i.e., it echoes the
 *   message back.  The device and sap are supplied by the
 *   user.  The program uses 802.3 format packets.
 *
 * Inputs:      device, sap.
 *
 * Outputs:     Exit status.
 *
 * To compile:  cc -o dli_802d dli_802d.c
 *
 * Example:     dli_802d de0 ac
 *
 * Comments:  This example demonstrates the recvfrom & sendto
 *   system calls.  Since packets may arrive from different
 *   systems we use the recvfrom call to read the packets.
 *   This call gives us access to the packet header information
 *   so that we can determine where the packet came from.
 *   When we write on the socket we must use the sendto
 *   system call to explicitly give the destination of
 *   the packet.  The use of the "DEFAULT" I/O control flag
 *   only applies (i.e. only has an affect) when the SNAP SAP
 *   is used.  When the SNAP SAP is used, any arriving packets
 *   which have the specified protocol id and which are not
 *   destined for some other program will be given to this
 *   program.
 */
 
/*
 * Compaq Computer Corporation supplies this software
 * example on an "as-is" basis for general customer use.
 * Note that Compaq does not offer any support for it, nor
 * is it covered under any of Compaq's support contracts.
 */
 
main(argc, argv, envp)
int argc;
char **argv, **envp;
 
{
 
    u_char inbuf[1500], outbuf[1500];
    u_char devname[16];
    u_char target_eaddr[6];
    char *cp;
    int rsize, devunit;
    int i, sock, fromlen;
    u_char tmpsap, sap;
    struct sockaddr_dl from;
    u_char *pi = 0;
 
    if ( argc < 3 )
    {
        fprintf(stderr, "usage: %s device hex-sap\n", argv[0]);
        exit(1);
    }
 
    /* get device name and unit number. */
    bzero(devname, sizeof(devname));
    i = 0;
    cp = argv[1];
    while ( isalpha(*cp) )
        devname[i++] = *cp++;
    sscanf(cp, "%d", &devunit);
 
    /* get sap */
    sscanf(argv[2], "%x", &sap);
 
    /* open dli socket. note that if (and only if) the snap sap */
    /* was selected then a protocol id must also be specified. */
    if ( sap == SNAP_SAP )
        pi = protocolid;
    if ((sock = dli_802_3_conn(devname, devunit, pi, target_eaddr,
                    DLI_DEFAULT, TYPE1, sap, sap, UI_NPCMD)) < 0) {
        perror("dli_802d, dli_conn failed");
        exit(1);
    }
 
    /* listen and respond */
    while ( 1 ) {
        /* wait for message */
        from.dli_family = AF_DLI;
        fromlen = sizeof(struct sockaddr_dl);
        if ((rsize = recvfrom(sock, inbuf, sizeof(inbuf), NULL,
                              &from, &fromlen)) < 0 ) {
            sprintf(inbuf, "%s: DLI reception failed", argv[0]);
            perror(inbuf);
            exit(2);
        }
 
        /* check header */
        if ( fromlen != sizeof(struct sockaddr_dl) ) {
            fprintf(stderr,"%s, incorrect header supplied\n",\
                 argv[0]);
            continue;
        }
 
        /*
         * Note that DLI swaps the source & destination saps and
         * lan addresses in the sockaddr_dl structure returned
         * by the recvfrom call.  That is, it places the DSAP in
         * eh_802.ssap and the SSAP in eh_802.dsap; it also places
         * the destination lan address in eh_802.src and the source
         * lan address in eh_802.dst.  This allows for minimal to
         * no manipulation of the address structure for subsequent
         * sendto or dli connection calls.
         */
 
        /* any data? */
        if ( ! rsize )
            fprintf(stderr, "%s: NO data received from ", \
                 argv[0]);
        else
            fprintf(stderr, "%s: data received from ", argv[0]);
        for ( i = 0; i < 6; i++ )
            fprintf(stderr, "%x%s",
                    from.choose_addr.dli_802addr.eh_802.dst[i],
                    ((i<5)?"-":" "));
        fprintf(stderr, "\n     on dsap %x ",
                from.choose_addr.dli_802addr.eh_802.ssap);
        if ( from.choose_addr.dli_802addr.eh_802.dsap == \
                 SNAP_SAP )
            fprintf(stderr,
               "(SNAP SAP), protocol id= %x-%x-%x-%x-%x\n    ",
               from.choose_addr.dli_802addr.eh_802.osi_pi[0],
               from.choose_addr.dli_802addr.eh_802.osi_pi[1],
               from.choose_addr.dli_802addr.eh_802.osi_pi[2],
               from.choose_addr.dli_802addr.eh_802.osi_pi[3],
               from.choose_addr.dli_802addr.eh_802.osi_pi[4]);
        fprintf(stderr, " from ssap %x ",
                from.choose_addr.dli_802addr.eh_802.dsap);
        fprintf(stderr, "\n\n");
 
        /* send response to originator. */
        if ( from.choose_addr.dli_802addr.eh_802.dsap == \
                 SNAP_SAP )
            bcopy(protocolid,
                  from.choose_addr.dli_802addr.eh_802.osi_pi, 5);
        if ( sendto(sock, inbuf, rsize, NULL, &from, fromlen) \
                 < 0 ) {
            sprintf(outbuf, "%s: DLI transmission failed", \
                 argv[0]);
            perror(outbuf);
            exit(2);
        }
    }
}
 
/*
 *              d l i _8 0 2 _ 3 _ c o n n
 *
 *
 *
 * Description:
 *      This subroutine opens a dli 802.3 socket, then binds an
 *      associated device name and protocol type to the socket.
 *
 * Inputs:
 *      devname         = ptr to device name
 *      devunit         = device unit number
 *      ptype           = protocol type
 *      taddr           = target address
 *      ioctl           = io control flag
 *      svc             = service class
 *      sap             = source sap
 *      dsap            = destination sap
 *      ctl             = control field
 *
 *
 * Outputs:
 *      returns         = socket handle if success, otherwise -1
 *
 *
 */
 
dli_802_3_conn (devname,devunit,ptype,taddr,ioctl,svc,sap,\
        dsap,ctl)
char *devname;
u_short devunit;
u_char *ptype;
u_char *taddr;
u_char ioctl;
u_char svc;
u_char sap;
u_char dsap;
u_short ctl;
{
    int i, sock;
    struct sockaddr_dl out_bind;
 
    if ( (i = strlen(devname)) >
         sizeof(out_bind.dli_device.dli_devname) )
    {
         fprintf(stderr, "dli_802d: bad device name");
         return(-1);
    }
 
    if ((sock = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0)
    {
         perror("dli_802d, can't open DLI socket");
         return(-1);
    }
 
    /*
     * fill out bind structure.  note that we need to determine
     * whether the ctl field is 8 bits (unnumbered format) or
     * 16 bits (informational/supervisory format).  We do this
     * by checking the low order 2 bits, which are both 1 only
     * for unnumbered control fields.
     */
    bzero(&out_bind, sizeof(out_bind));
    out_bind.dli_family = AF_DLI;
    out_bind.dli_substructype = DLI_802;
    bzero(&out_bind, sizeof(out_bind));
    out_bind.dli_family = AF_DLI;
    out_bind.dli_substructype = DLI_802;
    bcopy(devname, out_bind.dli_device.dli_devname, i);
    out_bind.dli_device.dli_devnumber = devunit;
    out_bind.choose_addr.dli_802addr.ioctl = ioctl;
    out_bind.choose_addr.dli_802addr.svc = svc;
    if(ctl & 3)
        out_bind.choose_addr.dli_802addr.eh_802.ctl.U_fmt=\
            (u_char)ctl;
    else
        out_bind.choose_addr.dli_802addr.eh_802.ctl.I_S_fmt = \
            ctl;
    out_bind.choose_addr.dli_802addr.eh_802.ssap = sap;
    out_bind.choose_addr.dli_802addr.eh_802.dsap = dsap;
    if ( ptype )
        bcopy(ptype,out_bind.choose_addr.dli_802addr.eh_802.osi_pi,\
            5);
    if ( taddr )
         bcopy(taddr, out_bind.choose_addr.dli_802addr.eh_802.dst,
               DLI_EADDRSIZE);
    if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
    {
         perror("dli_802d, can't bind DLI socket");
         return(-1);
    }
 
    return(sock);
}
 
 

F.5.5    getsockopt および setsockopt を使用した DLI プログラム例

#ifndef lint
static  char  *sccsid = "@(#)dli_setsockopt.c   1.5  3/27/90";
#endif lint
 
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dli/dli_var.h>
#include <sys/ioctl.h>
 
extern int errno;
int debug = 0;
 
#define PROTOCOL_ID      {0x00, 0x00, 0x00, 0x00, 0x5}
#define CUSTOMER0        {0xab, 0x00, 0x04, 0x00, 0x00, 0x00}
#define CUSTOMER1        {0xab, 0x00, 0x04, 0x00, 0x00, 0x01}
 
u_char mcast0[] = CUSTOMER0;
u_char mcast1[] = CUSTOMER1;
u_char protocolid[] = PROTOCOL_ID;
 
/*
 *
 *      d l i  e x a m p l e : d l i  s e t s o c k o p t
 *
 * Description: This program demonstrates the use of the DLI
 *    get- and setsockopt calls.  It opens a socket, enables
 *    2 multicast addresses, changes the 802 control
 *    field, enables a number of group saps supplied by
 *    the user, and reads the group saps that are enabled.
 *
 * Inputs:      device, sap, group-saps.
 *
 * Outputs:     Exit status.
 *
 * To compile:  cc -o dli_setsockopt dli_setsockopt.c
 *
 * Example:     dli_setsockopt qe0 ac  5 9 d
 *
 * Comments:    When a packet arrives with a group dsap,
 *    all dli programs that have that group sap enabled will
 *    receive copies of that packet.  Group saps are
 *    those with the low order bit set.  Group sap 1
 *    is currently not allowed for customer use. Group
 *    saps with the second bit set (eg 3,7,etc) are
 *    reserved by IEEE.
 */
 
/*
 * Compaq Computer Corporation supplies this software example
 * on an "as-is" basis for general customer use.  Note that
 * Compaq does not offer any support for it, nor is it covered
 * under any of Compaq's support contracts.
 */
 
main(argc, argv, envp)
int argc;
char **argv, **envp;
 
{
 
    u_char inbuf[1500], outbuf[1500];
    u_char devname[16];
    u_char target_eaddr[6];
    char *cp;
    int rsize, devunit;
    int i, j, k, sock, fromlen;
    u_short obsiz;
    u_char tmpsap, sap;
    struct sockaddr_dl from;
    u_char *pi = 0;
    u_char out_opt[1000], in_opt[1000];
    int optlen, ioptlen = sizeof(in_opt);
 
    if ( argc < 4 )
    {
        fprintf(stderr, "usage: %s device hex-sap hex-groupsaps\n",
        argv[0]);
        exit(1);
    }
 
    /* get device name and unit number. */
    bzero(devname, sizeof(devname));
    i = 0;
    cp = argv[1];
    while ( isalpha(*cp) )
    devname[i++] = *cp++;
    sscanf(cp, "%d", &devunit);
 
    /* get protocol type */
    sscanf(argv[2], "%x", &sap);
 
    /* open dli socket */
    if ( sap == SNAP_SAP ) {
        fprintf(stderr,
                "%s: can't use SNAP_SAP in USER mode\n", argv[0]);
        exit(1);
    }
    if ( (sock = dli_802_3_conn(devname, devunit, pi,\
           target_eaddr,
                     DLI_DEFAULT, USER, sap, sap, UI_NPCMD)) \
           < 0 ) {
        perror("dli_setsockopt: dli_conn failed");
        exit(1);
    }
 
    /* enable two multicast addresses */
    bcopy(mcast0, out_opt, sizeof(mcast0));
    bcopy(mcast1, out_opt+sizeof(mcast0), sizeof(mcast1));
 
    if ( setsockopt(sock, DLPROTO_DLI, DLI_MULTICAST, \
           &out_opt[0],
                    (sizeof(mcast0) + sizeof(mcast1))) < 0 ) {
        perror("dli_setsockopt: can't enable multicast");
    }
 
    /* set 802 control field */
    out_opt[0] = TEST_PCMD;
    optlen = 1;
    if
    (setsockopt(sock,DLPROTO_DLI,DLI_SET802CTL,&out_opt[0],\
           optlen)<0){
        perror("dli_setsockopt: Can't set 802 control");
        exit(1);
    }
 
    /* enable GSAPs supplied by user */
    j = 3;
    i = 0;
    while (j < argc ) {
        sscanf(argv[j++], "%x", &k);
        out_opt[i++] = k;
    }
    optlen = i;
    if
    (setsockopt(sock,DLPROTO_DLI,DLI_ENAGSAP,&out_opt[0],\
           optlen) < 0){
        perror("dli_setsockopt: Can't enable gsap");
        exit(1);
    }
 
    /* verify all gsaps are enabled */
    bzero(in_opt, (ioptlen = sizeof(in_opt)));
    if
    (getsockopt(sock,DLPROTO_DLI,DLI_GETGSAP,in_opt,\
           &ioptlen) < 0){
        perror("dli_setsockopt: DLI getsockopt 2 failed");
        exit(1);
    }
    printf("number of enabled GSAPs = %d, GSAPS:", ioptlen);
    for(i = 0; i < ioptlen; i++) {
        if ( ! (i % 10) )
            printf("\n");
        printf("%2x ",in_opt[i]);
    }
    printf("\n");
 
    /* disable all but the last 4 or all GSAPs, */
    /* whichever is smallest */
    if ( optlen > 4 )
        optlen -= 4;
    if
    (setsockopt(sock,DLPROTO_DLI,DLI_DISGSAP,&out_opt[0],\
           optlen) < 0){
        perror("dli_setsockopt: Can't disable gsap");
    }
 
    /* verify some gsaps still enabled */
    bzero(in_opt, (ioptlen = sizeof(in_opt)));
    if
    (getsockopt(sock,DLPROTO_DLI,DLI_GETGSAP,in_opt,\
           &ioptlen) < 0){
        perror("dli_setsockopt: getsockopt 3 failed");
        exit(1);
    }
    printf("number of enabled GSAPs = %d, GSAPS:", ioptlen);
    for(i = 0; i < ioptlen; i++) {
        if ( ! (i % 10) )
    printf("\n");
        printf("%2x ",in_opt[i]);
    }
    printf("\n");
 
}
 
/*
 *              d l i _8 0 2 _ 3 _ c o n n
 *
 *
 *
 * Description:
 *      This subroutine opens a dli 802.3 socket and then binds
 *      an associated device name and protocol type to it.
 *
 * Inputs:
 *      devname    = ptr to device name
 *      devunit    = device unit number
 *      ptype      = protocol type
 *      taddr      = target address
 *      ioctl      = io control flag
 *      svc        = service class
 *      sap        = source sap
 *      dsap       = destination sap
 *      ctl        = control field
 *
 *
 * Outputs:
 *      returns    = socket handle if success, otherwise -1
 *
 */
 
dli_802_3_conn (devname,devunit,ptype,taddr,ioctl,svc,sap,\
       dsap,ctl)
char *devname;
u_short devunit;
u_char *ptype;
u_char *taddr;
u_char ioctl;
u_char svc;
u_char sap;
u_char dsap;
u_short ctl;
{
    int i, sock;
    struct sockaddr_dl out_bind;
 
    if ( (i = strlen(devname)) >
         sizeof(out_bind.dli_device.dli_devname) )
    {
        fprintf(stderr, "dli_setsockopt: bad device name");
        return(-1);
    }
 
    if ((sock = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0)
    {
        perror("dli_setsockopt: can't open DLI socket");
        return(-1);
    }
 
    /*
     * Fill out bind structure
     */
    bzero(&out_bind, sizeof(out_bind));
    out_bind.dli_family = AF_DLI;
    out_bind.dli_substructype = DLI_802;
    bcopy(devname, out_bind.dli_device.dli_devname, i);
    out_bind.dli_device.dli_devnumber = devunit;
    out_bind.choose_addr.dli_802addr.ioctl = ioctl;
    out_bind.choose_addr.dli_802addr.svc = svc;
    if(ctl & 3)
        out_bind.choose_addr.dli_802addr.eh_802.ctl.U_fmt=\
            (u_char)ctl;
    else
        out_bind.choose_addr.dli_802addr.eh_802.ctl.I_S_fmt = \
            ctl;
    out_bind.choose_addr.dli_802addr.eh_802.ssap = sap;
    out_bind.choose_addr.dli_802addr.eh_802.dsap = dsap;
    if ( ptype )
        bcopy(ptype,out_bind.choose_addr.dli_802addr.eh_802.osi_pi,\
            5);
    if ( taddr )
        bcopy(taddr, out_bind.choose_addr.dli_802addr.eh_802.dst,
              DLI_EADDRSIZE);
    if ( bind(sock, &out_bind, sizeof(out_bind)) < 0 )
    {
        perror("dli_setsockopt: can't bind DLI socket");
        return(-1);
    }
 
    return(sock);
}