SW 개발

linux / 2.4 / DD / 리눅스 네트워크 드라이버

. . . 2010. 8. 10. 16:13
반응형

  * 출처 : 인터넷 어디선가...;
  * 인터넷에서 많이 퍼져있는 자료를 원문그대로 보기좋게 편집하여서 포스팅 합니다.
  * 백업용 포스팅이므로 역시나 문제가 된다면 자삭하겠습니다.

===========================================================================================================

이야기꾼:이기천(hetta@nownuri.net)

  *강좌에 앞서서....     

   이 글은 GFDL(GNU Free Documentation License)을 따릅니다.  그러므로 필자는 이글로 인한 물질적이든 정신적이든 또한 가정의 불화든지간에 어떠한 책임도 지지 않습니다. 원저자가 명시되는한 비상업적용도에 한하여 자유로운 배포,수정이가능하지만, 어떠한 수단이든지간에 상업적 용도로 사용되어질수는 없습니다.

<참고자료>
1.Linux Device Driver 2nd
  - http://www.oreilly.com 에서 pdf버전 무료배포
2.Linux Network Device Driver(권수호)
  - http://www.linuxdeveloper.co.kr
3.RTL8139C datasheet
  - http://www.realtek.com.tw/
4.(Linux_Kernel_Src_Path)drivers/net/8139too.c
5.RTL8139 Programming guide
6.Understanding the Linux Kernel

*환경:kernel 2.4.9
(다른버전의 커널에서는 조금씩 다를수있음을 유의하시기 바랍니다.)

오늘은 이번강의의 첫번째시간입니다. 이번시간엔 먼저 Network Driver에대한 개념을 잡기위하여 Linux Device Driver 2nd의 [chapter 15. Network Drivers]를 기반으로  이야기를 시작하겠습니다. 언제까지 할것이라는 약속은 못드릴것같습니다.저도 인간이니까요..어쩌면 이번강의 가 처음이자 마지막일지도 모르겠죠^^  언제든지 네트워크 드라이버 분석이 재미(funny)없다면 강의를 그만둘겁니다.(아마 가능성이 희박하겠지만^^)

저도 초보임을 밝히겠습니다.  실제적 device driver를 다루는것은 처음입니다. 그러므로 틀린점이 많을것같습니다...언제든지 잘못된것은 혼자알지마시고 알려주세여 ~~~~~~~~~!저는 제가 이해한 방향으로 강좌를 진행하겠습니다.^^
이제부터 존칭은 생략하도록 하겠습니다.^^ 타이핑치기가 힘드러서요.... :P 자 그럼 시작합니다. :)

모듈 올리기
^^^^^^^^^^^^
디바이스 드라이버가 커널에 로딩될때 자기가 사용할 리소스를 커널에 요청하게 된다. 예를들어 사용할 irq번호와 I/O port를 등록해야한다. 랜카드를 잡을때 다음과 같이 잡는다. 필자의 경우도 ne2000랜카드를 잡을때, 먼저 윈도우로 가서 거기서 io와 irq번호를 알아온다음에 리눅스에서 다음과 같이 잡았다.(물론 이렇게 잡는것은 isa방식이 대부분이었으나, pci도 이렇게 잡 아야 잡히는 경우를 경험했다.)

modprobe ne.o io=0x100 irq=9

이것이  랜카드가 사용할 io와 irq를 명령어 라인 인수로 넘겨받게 되는것이다. 크게 디바이스 드라이버는... 불록(block),문자(character),네트웍 이렇게 3개로 나눈다. 그중에서 불록과 문자는 주번호와 부번호라는것이 존재하여 디바이스를 제어하나, 네트웍장치는 그러한 개념이 존재하지 않는다.그대신 커널의 어느곳에서도 참조할수있는 전역(global)리스트로 새로운 디바이스장치를 넣게된다.

또한 "유닉스(리눅스)에서 는 모든것이 파일"이라는 말을 많이 들어보았을것이다. 하지만, 여기서 네트웍장치 는 아쉽게도 예외로서 존재한다. 다른 문자나,블록장치는 /dev/밑에 실제적인 파일로서존재하나, 네트웍장치만은 거기에 존재하
지 않는다! 뭐.. 예외없는 법칙이 없다는말도 있으니, 잠시 봐주기로 하자..ㅎㅎ

"유닉스에서 모든것은 파일이다.(단 네트웍장치만빼고^^)"
(주: 네트웍 장치말고 다른것이 있다면 알려주기 바랍니다.)

위에서 전역리스트가 존재한다고 하였다. 그렇다면 이 전역리스트의 자료구조는 어떻게 생겼을까?

리눅스는 네트웍 장치에 대한 데이터를 struct net_device로서 관리한다. 이제, LXR로 살펴보도록하자.

<참고> -----------------------------------------------------------
모르시는 분을 위하여 간단히 쓰겠다.
웹브라우저로 http://lxr.linux.no에가서 browse the code를 누른뒤 identifier search 를 누르고 검색란에 net_device를 적는다.이제 net_device를 정의한  다음과 같은 검색결과가 나올것이다. 그것을 누른다.
include/linux/netdevice.h, line 230
--------------------------------------------------------------------

커널소스를 처음보시는분은 벌써부터 기가 찰지도 모르겠다. 구조체가 자그마치 180라인이니 말이다. 그러나 모든것을 알필요는없다. 필요한것은 필요할때 공부한다는 맘을 갖고, 대충만 훌터보고 넘어가도록하자. 뒤에서 꼭 필요한 필드만을 설명할것이다. (사실 안봐도 이야기 진행상 큰문제는없으니 너무 겁부터 내지는 말기를 바란다.)

----------------------------------------------------------------------
/*
 *  The DEVICE structure.
 *  Actually, this whole structure is a big mistake.  It mixes I/O
 *  data with strictly "high-level" data, and it has to know about
 *  almost every data structure used in the INET module.
 *
 *  FIXME: cleanup struct net_device such that network protocol info
 *  moves out.
 */

struct net_device
{

    /*
     * This is the first field of the "visible" part of this structure
     * (i.e. as seen by users in the "Space.c" file).  It is the name
     * the interface.
     */
    char            name[IFNAMSIZ];

    /*
     *  I/O specific fields
     *  FIXME: Merge these and struct ifmap into one
     */
    unsigned long       rmem_end;   /* shmem "recv" end */
    unsigned long       rmem_start; /* shmem "recv" start   */
    unsigned long       mem_end;    /* shared mem end   */
    unsigned long       mem_start;  /* shared mem start */
    unsigned long       base_addr;  /* device I/O address   */
    unsigned int        irq;        /* device IRQ number    */

    /*
     *  Some hardware also needs these fields, but they are not
     *  part of the usual set specified in Space.c.
     */

    unsigned char       if_port;    /* Selectable AUI, TP,..*/
    unsigned char       dma;        /* DMA channel      */

    unsigned long       state;

    struct net_device   *next;

    /* The device initialization function. Called only once. */
    int         (*init)(struct net_device *dev);

    /* ------- Fields preinitialized in Space.c finish here ------- */

    struct net_device   *next_sched;

    /* Interface index. Unique device identifier    */
    int         ifindex;
    int         iflink;

    struct net_device_stats* (*get_stats)(struct net_device *dev);
    struct iw_statistics*   (*get_wireless_stats)(struct net_device *dev);

    /*
     * This marks the end of the "visible" part of the structure. All
     * fields hereafter are internal to the system, and may change at
     * will (read: may be cleaned up at will).
     */

    /* These may be needed for future network-power-down code. */
    unsigned long       trans_start;    /* Time (in jiffies) of last Tx */
    unsigned long       last_rx;    /* Time of last Rx  */

    unsigned short      flags;  /* interface flags (a la BSD)   */
    unsigned short      gflags;
    unsigned        mtu;    /* interface MTU value      */
    unsigned short      type;   /* interface hardware type  */
    unsigned short      hard_header_len;    /* hardware hdr length  */
    void            *priv;  /* pointer to private data  */

    struct net_device   *master; /* Pointer to master device of a group,
                      * which this device is member of.
                      */

    /* Interface address info. */
    unsigned char       broadcast[MAX_ADDR_LEN];    /* hw bcast add */
    unsigned char       dev_addr[MAX_ADDR_LEN]; /* hw address   */
    unsigned char       addr_len;   /* hardware address length  */

    struct dev_mc_list  *mc_list;   /* Multicast mac addresses  */
    int         mc_count;   /* Number of installed mcasts   */
    int         promiscuity;
    int         allmulti;

    int         watchdog_timeo;
    struct timer_list   watchdog_timer;

    /* Protocol specific pointers */

    void            *atalk_ptr; /* AppleTalk link   */
    void            *ip_ptr;    /* IPv4 specific data   */
    void                    *dn_ptr;        /* DECnet specific data */
    void                    *ip6_ptr;       /* IPv6 specific data */
    void            *ec_ptr;    /* Econet specific data */

    struct Qdisc        *qdisc;
    struct Qdisc        *qdisc_sleeping;
    struct Qdisc        *qdisc_list;
    struct Qdisc        *qdisc_ingress;
    unsigned long       tx_queue_len;   /* Max frames per queue allowed */

    /* hard_start_xmit synchronizer */
    spinlock_t      xmit_lock;
    /* cpu id of processor entered to hard_start_xmit or -1,
       if nobody entered there.
     */
    int         xmit_lock_owner;
    /* device queue lock */
    spinlock_t      queue_lock;
    /* Number of references to this device */
    atomic_t        refcnt;
    /* The flag marking that device is unregistered, but held by an user */
    int         deadbeaf;

    /* Net device features */
    int         features;
#define NETIF_F_SG      1   /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM     2   /* Can checksum only TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM     4   /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM     8   /* Can checksum all the packets. */
#define NETIF_F_DYNALLOC    16  /* Self-dectructable device. */
#define NETIF_F_HIGHDMA     32  /* Can DMA to high memory. */
#define NETIF_F_FRAGLIST    64  /* Scatter/gather IO. */

    /* Called after device is detached from network. */
    void            (*uninit)(struct net_device *dev);
    /* Called after last user reference disappears. */
    void            (*destructor)(struct net_device *dev);

    /* Pointers to interface service routines.  */
    int         (*open)(struct net_device *dev);
    int         (*stop)(struct net_device *dev);
    int         (*hard_start_xmit) (struct sk_buff *skb,
                            struct net_device *dev);
    int         (*hard_header) (struct sk_buff *skb,
                        struct net_device *dev,
                        unsigned short type,
                        void *daddr,
                        void *saddr,
                        unsigned len);
    int         (*rebuild_header)(struct sk_buff *skb);
#define HAVE_MULTICAST           
    void            (*set_multicast_list)(struct net_device *dev);
#define HAVE_SET_MAC_ADDR        
    int         (*set_mac_address)(struct net_device *dev,
                           void *addr);
#define HAVE_PRIVATE_IOCTL
    int         (*do_ioctl)(struct net_device *dev,
                        struct ifreq *ifr, int cmd);
#define HAVE_SET_CONFIG
    int         (*set_config)(struct net_device *dev,
                          struct ifmap *map);
#define HAVE_HEADER_CACHE
    int         (*hard_header_cache)(struct neighbour *neigh,
                             struct hh_cache *hh);
    void            (*header_cache_update)(struct hh_cache *hh,
                               struct net_device *dev,
                               unsigned char *  haddr);
#define HAVE_CHANGE_MTU
    int         (*change_mtu)(struct net_device *dev, int new_mtu);

#define HAVE_TX_TIMEOUT
    void            (*tx_timeout) (struct net_device *dev);

    int        (*hard_header_parse)(struct sk_buff *skb,
                             unsigned char *haddr);
    int        (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
    int         (*accept_fastpath)(struct net_device *, struct dst_entry*);

    /* open/release and usage marking */
    struct module *owner;

    /* bridge stuff */
    struct net_bridge_port  *br_port;

#ifdef CONFIG_NET_FASTROUTE
#define NETDEV_FASTROUTE_HMASK 0xF
    /* Semi-private data. Keep it at the end of device struct. */
    rwlock_t        fastpath_lock;
    struct dst_entry    *fastpath[NETDEV_FASTROUTE_HMASK+1];
#endif
#ifdef CONFIG_NET_DIVERT
    /* this will get initialized at each interface type init routine */
    struct divert_blk   *divert;
#endif /* CONFIG_NET_DIVERT */
};
----------------------------------------------------------------------

실제적인 네트웍장치 등록은 이 net_device의 필드들에 적절한 값을 세팅한다음 다음과 같이 register_netdev()를 호출하는것이다.

int result ;
struct net_device *my_net ;
// my_net그조체에 적절한 값을 채움!
result = register_netdev( my_net );

<참고>----------------------------------------------------------------
LDD(Linux Device Driver) 15챕터에서는 snull이라는 장치를 만드는데, 이장치는 실제적인 하드웨어가 아닌 가상장치이다. 개인적으로 snull이라는 장치가 동작하는방식을 이해하는것은 별 도움이 안되었다. 그래서 불필요한 혼동을 줄이고자 되도록이면, LDD에 나오는 snull의 설계는 제외하고, 실제적 네트웍장치를 분석하는데 도움이 되는 부분만을 설명할 것이다.  전체적인 흐름을 이해하는것이 중요할것같다.
관심있으신분은 오렐리 사이트에서 snull소스를 다운받아서 살펴보기 바란다.
-----------------------------------------------------------------------

네트웍 장치를 초기화하기
~~~~~~~~~~~~~~~~~~~~~~~~~

이때 snull의 경우  net_device구조체에 지정한 init함수가 초기화를 수행한다. 그함수는 snull_init이며, net_device구조체에 적절한 필드를 채운다.

예를들어

dev->open = snull_open ; //장치 열때.      아마도 ifconfig eth0 up 일듯?
dev->stop = snull_release ; //장치 닫을때. 아마도 ifconfig eth0 down 일듯?
dev->do_ioctl = snull_ioctl ; //ioctl시스템 콜할때
dev->hard_start_xmit = snull_tx ;
dev->get_stats = snull_stats ;
dev->tx_timeout = snull_tx_timeout ; //Tx타임아웃발생시 호출함수
dev->watchdog_timeo = timeout ; //timeout 되는 시간설정
//....
SET_MODULE_OWNER(dev)
//등등...

open은 장치를 열때 실행되는것이며 , hard_start_xmit은 패킷을 전송하기전에 초기화를 하는것이며, get_stats은 장치에 관한 정보를 요청할때 정보를 제공 하는것이다. ifconfig 라는 명령어를 칠때 get_stats가 실행된다고 보면
될것이다.
SET_MODULE_OWNER()은 net_device의 owner 필드에 이 디바이스 모듈에 대한 포인터로 초기화한다.  모듈의 usage count를 관리하기 위하여, file_operations구조체의 owner필드와 똑같이 커널에서 사용한다.

예를들자면 ifconfig eth0 up할때  snull_open이 실행되고, ifconfig eth0 down할때 snull_release가 실행된다고 보면 될것이다.

<참고>-----------------------------------------------------------------
하지만, 실제 8139too.c에서는 약간 다른방식을 사용하는것으로 보인다. 즉, 장치를 초기화 하는것이 net_device의 init필드를 사용하는것이 아니라, pci_driver 의 probe를 사용한다는것이다.( rtl8139_init_one() ) 하지만 하는 작업은 비슷하다고 생각하면 될것같다. rtl8139_init_one(...)에서 결국 net_device를 채운다음 register_netdev()를 호출한다.
-----------------------------------------------------------------------

주의할점은 irq와 io같은 중요한 리소스는 init하는시점이 아닌 open시점에 리소스를 확보해야한다는것이다. 왜냐하면 그렇지 않을경우 실제로 사용되지도 않는 장치에 할당된 io와 irq때문에 다른장치를 쓰지 못하는경우도 발생할수있기
때문이다.

또한 net_device에는 priv라는 필드가 있다. 이것은 각각의 장치가 관리하는 private한 자료등을 관리하게 된다. 이것은 장치를 open하는 시점이 아니라 init할때 실행해야한다.왜냐하면 priv에는 각종통계정보에 유용한 값들이 있는데
사용자는 장치가 활성화되지 않았을때라도 그정보를 보기 원할수있기 때문이다.

snull의 경우에는 다음과 같은 필드들이 있다.

------------------------------------------------------------------
/*
 * This structure is private to each device. It is used to pass
 * packets in and out, so there is place for a packet
 */

struct snull_priv {
    struct net_device_stats stats;
    int status;
    int rx_packetlen;
    u8 *rx_packetdata;
    int tx_packetlen;
    u8 *tx_packetdata;
    struct sk_buff *skb;
    spinlock_t lock;
};

여기서 새로운 구조체가 나오는데 net_device_stats로서 말그대로 네트웍장치에대한 통계자료를 관리한다.

-----------------------------------------------------------------
/*
 *  Network device statistics. Akin to the 2.0 ether stats but
 *  with byte counters.
 */

struct net_device_stats
{
    unsigned long   rx_packets;     /* total packets received   */
    unsigned long   tx_packets;     /* total packets transmitted    */
    unsigned long   rx_bytes;       /* total bytes received     */
    unsigned long   tx_bytes;       /* total bytes transmitted  */
    unsigned long   rx_errors;      /* bad packets received     */
    unsigned long   tx_errors;      /* packet transmit problems */
    unsigned long   rx_dropped;     /* no space in linux buffers    */
    unsigned long   tx_dropped;     /* no space available in linux  */
    unsigned long   multicast;      /* multicast packets received   */
    unsigned long   collisions;

    /* detailed rx_errors: */
    unsigned long   rx_length_errors;
    unsigned long   rx_over_errors;     /* receiver ring buff overflow  */
    unsigned long   rx_crc_errors;      /* recved pkt with crc error    */
    unsigned long   rx_frame_errors;    /* recv'd frame alignment error */
    unsigned long   rx_fifo_errors;     /* recv'r fifo overrun      */
    unsigned long   rx_missed_errors;   /* receiver missed packet   */

    /* detailed tx_errors */
    unsigned long   tx_aborted_errors;
    unsigned long   tx_carrier_errors;
    unsigned long   tx_fifo_errors;
    unsigned long   tx_heartbeat_errors;
    unsigned long   tx_window_errors;

    /* for cslip etc */
    unsigned long   rx_compressed;
    unsigned long   tx_compressed;
};
-----------------------------------------------------------------------

뭐 간단히 예를 들자면 패킷을 받다가 메모리가 꽉차서 drop되었다면 rx_dropped 필드를 하나 증가시켜주는 그냥 이런식이다. ifconfig명령을 내리면 랜카드에대한 각종 통계치가 나오는데 이런통계자료를 바탕으로 출력되는것이다.

 

eth0      Link encap:Ethernet  HWaddr 00:50:FC:0C:ff:f1
          ...
          RX packets:894551 errors:0 dropped:0 overruns:0 frame:0
          TX packets:686533 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          Interrupt:5 Base address:0xac00

private한 구조체를 관리하려면 당연히 먼저 메모리공간을 확보해야한다.
다음과 같이 한다.

dev->priv  = kmalloc(sizeof(struct snull_priv), GFP_KERNEL);
if( dev->priv == NULL )
    return -ENOMEM ;
memset( dev->priv, 0 sizeof(struct snull_priv));
spin_lock_init( &((struct snull_priv *) dev->priv)->lock ) ;

마지막에서 spin_lock_init()은 스핀락인 snull_priv의 lock을 초기화하는것이다.
일단 스핀락은 그냥 race condition을 방지하는것이라고만 이해하도록 하자.

모듈내리기
^^^^^^^^^^^
모듈을 내리려면 다음과 같이 priv에 할당된 메모리를 해제하고
unregister_netdev()로서 네트웍장치의 등록을 해제한다.

void snull_cleanup(void)
{   
    int i;
    for (i=0; i<2;  i++) {
        kfree(snull_devs[i].priv);
        unregister_netdev(snull_devs + i);
    }
    return;
}

net_device구조체에 대해서...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
위에서 net_device에 대하여 조금 언급하였다.여기서는 조금더 깊이 들어가 보도록 하겠다.

이 구조체는 위에서 보았듯이 상당히 길이가 길다. 크게 두가지로 나뉘는데, 첫번째는 보이는(visible)필드과 숨겨진(hidden)필드이다.보이는 필드는 그냥 static구조체에 명시적으로 값을 할당할수있는 필드들로 구성된다.(the visible
part of the structure is made up of the fields that can be explicitly assigned in static net_device structure ) 그냥 쉽게 말해 보이는필드는 우리가 신경써야되는부분이며, 숨겨진 필드는 커널에서 내부적으로 사용되는것이라 생각하면 될것같다.

먼저 보이는필드를 살펴보자.

char name[IFNAMSIZ];
   이것은 장치의 이름을 지칭한다. 예를들어 이더넷장치로 eth0,eth1,eth2등을  보았을것이다. 여기에 %d를 넣으면 0부터 장치이름이 지어진다.  즉 "eth%d"로 한다면 맨 첫번째 장치는 eth0가되고 그담이 eth1...이런식이다.

unsigned long rmem_end ;
unsigned long rmem_start;
unsigned long mem_end ;
unsigned long mem_start ;
  
장치가 사용하는 메모리 정보이다. 만약 패킷을 받을때 사용되는 rx와 패킷을 보낼때 사용하는 tx에서 다른영역이 할당되었다면 rmem_ 은 rx를 의미하고 mem_은 tx를 의미한다. mem_start, mem_end는 부팅할때 명령어 라인에서 지정할수있으며, 그값은 ifconfig로서 확인할수있다. end - start함으로서 사용되는 메모리를 확인할수있다.

ifconfig의 맨페이지에 다음과 같은 설명이 나온다.

    mem_start addr
        Set  the  start  address  for shared memory used by
        this device.  Only a few devices need this.

즉 mem_start를 이용해 ifconfig에서 값을 변경할수있다는것이다.

unsigned long base_addr ;
   랜카드가 사용하는 I/O 주소이다.이것도 ifconfig로 확인가능하다.

unsigned char irq ;
   할당된 인터럽트 번호이다. ifconfig로 확인가능하다.

unsigned char if_port ; 
  
이것은 두개이상의 포트가 있는 랜카드의 경우 어느것이 사용되는가를 나타낸다. 예를들어 coaxial , twisted-pair의 두개가 있을경우이다.  (IF_PORT_10BASE2 , IF_PORT_10BASET )

unsigned char dma  ;
   장치에 할당된 dma채널이다.이것은 isa의 경우에 쓰이며 pci는 안쓰인다.

unsigned long state ;
   장치의 상태이다.이값은 보통 직접 접근하여 수정하지 않고, 이값을 관리하는 함수가 따로 존재한다.

struct net_device *next ;
   앞에서 네트웍장치는 전역 리스트로 관리된다고 했다. 이것은 그 전역리스트에서 다음 장치를 지칭한다. 이것은 수정되어서는 안된다.

int (*init)(struct net_device *dev)
   초기화 함수이다. (참고: rtl8139는 이걸 사용하지 않는다.)

...

이제 숨겨진 필드들을 살펴보도록 하자.
일반적으로 이 필드들은 드라이버를 초기화할때 할당된다.

unsigned mtu ;
maximum transfer unit(MTU)이다. 이것은 network layer에서 사용되는것이다.

    즉, IP layer 에서의 패킷의 크기를 제한하는데 이더넷의경우 1500이다. 그렇다면 실제적으로 네트웍라인상에 보내지는 패킷의 최대크기는 몇일까?  :  mtu(1500) + ethernet header(14) + pad(4) = 1536 이다.

unsigned long tx_queue_len ;
   장치의 전송큐에 넣을수있는 최대한의  프레임수이다.이값은 이더넷의경우 100으로 할당되어있으나,변경할수도있다. ifconfig로 확인가능하다.

eth0      Link encap:Ethernet  HWaddr 00:50:FC:0C:ff:ff
          ...
          collisions:0 txqueuelen:100
          Interrupt:5 Base address:0xac00

unsigned char addr_len ;
unsigned char broadcast[MAX_ADDR_LEN];
unsigned char dev_addr[MAX_ADDR_LEN];
  
addr_len은 주소의 길이로 이더넷의경우 6 옥텟(octets)이다. broadcast는 6개의 옥텟이 0xff로 채워짐으로서 이뤄진다. dev_addr 은 하드웨어 주소를 나타내는것으로 이값은 랜카드마다 고유하게 갖고있게되며, 모듈초기화시 랜카드의 EEPROM에서 읽어오게된다.

<참고>---------------------------------------------------------
네트웍에서는 byte대신 octet이라는 용어가 사용된다.
그냥 똑같이 이해하면 별상관은 없을것이다.
---------------------------------------------------------------

unsigned short flags ;
  
이 플래그는 각각의 장치의 독특한 속성을 지정하게 된다. 앞에 IFF_라는 prefix가 있으며, <linux/if.h>에 모두 선언되어있다. 몇몇은 커널에의해 내부적으로 사용되고,몇몇은 초기화시 사용된다. 다음과 같은 값들이 가능하다.

----------------------------------------------------------------------
/* Standard interface flags. */
#define IFF_UP      0x1     /* interface is up      */
#define IFF_BROADCAST   0x2     /* broadcast address valid  */
#define IFF_DEBUG   0x4     /* turn on debugging        */
#define IFF_LOOPBACK    0x8     /* is a loopback net        */
#define IFF_POINTOPOINT 0x10        /* interface is has p-p link    */
#define IFF_NOTRAILERS  0x20        /* avoid use of trailers    */
#define IFF_RUNNING 0x40        /* resources allocated      */
#define IFF_NOARP   0x80        /* no ARP protocol      */
#define IFF_PROMISC 0x100       /* receive all packets      */
#define IFF_ALLMULTI    0x200       /* receive all multicast packets*/

#define IFF_MASTER  0x400       /* master of a load balancer    */
#define IFF_SLAVE   0x800       /* slave of a load balancer */

#define IFF_MULTICAST   0x1000      /* Supports multicast       */

#define IFF_VOLATILE    (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MASTER|IFF_SLAVE|IFF_RUNNING)

#define IFF_PORTSEL 0x2000          /* can set media type       */
#define IFF_AUTOMEDIA   0x4000      /* auto media select active */
#define IFF_DYNAMIC 0x8000      /* dialup device with changing addresses*/
------------------------------------------------------------------------

다른값들은 참고만하고 IFF_PROMISC,IFF_ALLMULTI 플래그는 꼭 알아두자. 왜냐하면 우리가 분석할 8139too.c에서 사용되기 때문이다.

  • IFF_PROMISC 
    이것은 자기의 하드웨어 주소에 해당하는 패킷뿐아니라.  랜에 돌아다니는 모든 패킷을 잡는 것이다. tcpdump는 이플래그를 이용하여 패킷을 모두 잡는다.
  • IFF_MULTICAST
    멀티캐스트 패킷을 보내는것을 가능하게한다. 이것은 디폴트로 "허용"이므로 만일 멀티캐스트패킷을 보내지 못하도록하려면, 초기화시 이플래그를 꺼주어야한다.
  • IFF_ALLMULTI 
    모든 멀티캐스트 패킷을 받도록한다.커널은 해당 호스트가 멀티캐스트 라우팅을 할때, IFF_MULTICAST가 켜져있는때에 한하여 이 플래그를 세팅한다. 이값은 read-only이다.

이러한 플래그들이 수정되면 제일먼저 net_device의 set_multicase_list가 실행된다.그러므로 플래그가 변경되었을때 실행되야할 작업이 있다면 set_multicast_list에 넣도록한다.

~~~~~~~~~~~~~~~~~~~~~~~~

이제 net_device필드들중에서 함수들을 살펴보도록하자.이미 몇가지는 앞에서 언급했지만,다시한번 살펴보는 마음으로 살펴보도록하자.

블록이나 문자장치와 같이 네트웍장치도 장치를 다루는 여러가지 함수들이 존재한다. 문자나 블록장치는 file_operations구조체에 이값들을 세팅하나, 네트웍장치는 net_device구조체에 세팅한다. 이값들중 어떤값은 NULL로 세팅 할수도 있다. 그럼 하나하나 알아보도록하자.

int (*open)(struct net_device *dev);
   ifconfig eth0 up 과 같은 명령을 했을때 실행되는 함수이다. 여기서는   I/O ports , IRQ, DMA등의 리소스를 등록,usage count증가 등의 작업을 한다.

int (*stop)(struct net_device *dev);
    ifconfig eth0 down과 같은 명령을 했을때 실행되며, open에서 할당받은 리소스 들을 반납하는 작업등을 한다.

int (*hard_start_xmit)(struct sk_buff *skb, struct net_device *dev)
    패킷을 외부로 전송하기위한 초기화 작업을 실행한다. 외부로 보내질  모든데이터는 sk_buff구조체에서 관리한다.

void (*tx_timeout)(struct net_device *dev);
    패킷을 전송하는것에 대한 timeout이다. 즉, 설정된 시간만큼 기다렸는데,  패킷이 전송되지 못했다면, 재전송등, 어떠한 작업을 해주어야 한다.

struct net_device_stats *(*get_stats)(struct net_device *dev );
   
장치에대한 정보를 요청할때 이함수가 실행된다. 예를들어...  ifconfig, netstat -i 등이 되겠다.

int (*do_ioctl)(struct net_device *dev, struct ifreg *ifr, int cmd);
    ioctl명령어.사용하지 않을려면  NULL로 지정해도 상관없다.

void (*set_multicast_list)(struct net_device *dev);
   네트웍장치에대한 멀티캐스트 리스트(multicast list)가 변경되었거나  플래그가 변경되었을때 실행된다.

*이제 장치에대한 유용한 정보등을 포함한 잡다한 필드들을 알아보자.

unsigned long trans_start ;
unsigned long last_rx ;
   
trans_start는 전송을 전송한 시간을 jiffies값으로 갖고있고, last_rx는 가장 최근에 패킷을 받은 시간을 jiffies값으로 갖고있다.

int watchdog_timeo ; 
   
이값도 jiffies값으로 지정되며, 이 시간동안 전송되지 못했다면, tx_timeout   함수가 실행된다.

void *priv ;
    장치의 private 데이터를 저장하는곳이다.

struct dev_mc_list *mc_list ;
int mc_count ;
   
이두개의 필드는 멀티캐스트 전송을 관리하는데 사용된다.

여기서 설명하지 않은 net_device 필드들이 많이 있지만, 그것들은 네트웍 드라이버에 사용되지 않는다. 또한 네트웍 드이버에 사용된다 하더라도, 우리의 목표인 8139too.c에 사용되지 않는것은 배제하였다. 필요한것은 필요한때... ^_^

반응형