리눅스2010. 5. 18. 17:29
반응형

7.3. 네트워크 디바이스 드라이버
 
 
    네크워크 디바이스 드라이버는 데이터의 수신과 전송을 담당한다. 실제적인 네트워크 장치를 제어 하며 상위 프로토콜 레이어에서 내려오는 데이터를 하위의 물리 레이어로 전달한다. 또한, 하위의 물리 레이어에서 올라오는 데이터를 프로토콜 레이어에서 인식할 수 있도록 적절한 연산을 하는 기능도 들어가 있다.
 
    커널은 명확히 정의된 인터페이스를 통해서 네트워크 장치에 접근을 시도하게 되는데 이러한 인터페이스의 구조를 보면 다음과 같다. 이 구조는 include/linux/netdevice.h 에 정의 되어 있는 net_device 구조체의 함수 포인터 필드들이 된다.
 
      + 표 7-3-1.네크워크 인터페이스 구조
   
함 수 내 용
open  네트워크 장치에 대해서 open을 하게 되며 대부분의 하드웨어에 대한 초기화가 일어나게 된다.
hard_start_xmit  데이터의 전송요구가 있을 경우에 호출이 되며 데이터를 버스상에 놓기 위한 기본적인 일을 행한다.
stop  장치에 대한 더 이상의 요구가 없을 때 커널에 의해 호출이 되는 장치를 close 하는 시점이 될 수 있다. stop을 하기전에 사용하는 모든 자원에 대한 해지를 해야 한다.
get_stats  장치에 대한 통계정보를 구하기 위해서 커널에 의해 호출이 되는데 커널은 이것과 관련된 정보를 수집해 주어야 한다.
set_multicast_list  멀티 캐스트 주소를 설정하기 위해서 커널에 의해 호출이 되며 만들고자 하는 장치에는 이것을 위한 레지스터를 가지고 있다.
do_ioctl  /O 제어에 대한 요구를 처리하는 부분이다.
 
 
    소켓은 어플리케이션과의 인터페이스를 제공하기 위한 API 이다. 소켓에서 사용하는 데이터들은 소켓 버퍼라는 형태로 만들어져서 리눅스에서 제공하는 프로토콜 스택의 처리를 받게 되며 최종적으로 네트워크 디바이스 드라이버에 도달하게 된다. 결국, 네트워크 디바이스 드라이버에서의 쓰기는 소켓 버퍼에 대한 것만 생각하면 된다. 이런 소켓버퍼 구조체의 필드들의 내용을 보면 다음과 같다.
 
      + 표 7-3-2. 소켓 버퍼 구조체의 내용
   
필 드 내 용
struct net_device *dev  소켓 버퍼가 전달되거나 보내진 장치를 가리킨다.
unsigned char *head,
unsigned char *data,
unsigned char *tail,
unsigned char *end
 네트워크를 통해서 전달되는 패킷의 데이터를 가리킨다.
unsigned long len  데이터 자체의 길이를 나타내는 것을 skb->tail - skb->head를 가진다.
 
 
    이것 이외에도 여러 필드가 존재하지만 그것들에 대해서는 다루어 주지 않아도 상관이 없다. 소켓 버퍼에서 사용하게 되는 함수로는 다음과 같은 것들이 있으며 이것이 프로토타입(Prototype)은 include/linux/skbuff.h에 선언이 되었으며 함수들은 net/core/skbuff.c에서 정의 되어있다.
 
      + 표 7-3-3. 소켓 버퍼에서 사용하게 되는 함수
   
함 수 내 용
struct sk_buff *alloc_skb(unsigned int len, int priority)  소켓 버퍼를 할당 한다.
struct sk_buff *dev_alloc_skb(unsigned int len)  소켓 버퍼를 할당하며 priority로 GFP_ATOMIC을 주고, head와 tail사이에 16바이트를 남겨둔다.
void kfree_skb(struct sk_buff *skb, int rw)  할당된 소켓 버퍼를 해지한다.
void dev_kfree_skb(struct sk_buff *skb, int rw)  할당된 소켓 버퍼를 해지하며 버퍼의 locking을 정확히 처리한다. 드라이버에서는 dev_kfree_skb 함수를 사용해서 소켓버퍼를 해지해야 한다.
unsigned char *skb_put(struct sk_buff *skb, int len)  버퍼의 끝에 데이터를 넣고 tail과 len field를 고친다.
int skb_tailroom(struct sk_buff *skb)  소켓 버퍼에 남은 데이터를 위한 영역의 크기를 리턴한다.
int skb_headroom(struct sk_buff *skb)  데이터 앞에 남은 부분의 사용 가능한 영역의 크기를 리턴한다.
int skb_reserve(struct sk_buff *skb, int len)  소켓 버퍼에 데이터를 쓰기 전에 headroom에 공간을 마련한다.
unsigned char *skb_pull(struct sk_buff *skb, int len)  패킷의 head로부터 데이터를 분리해 낸다.
 
 
    위와 같은 함수들을 사용해서 커널로부터 넘겨받은 소켓 버퍼에 대한 연산을 하며, 또한 받은 packet에 대해서 커널로 넘겨주기 전에 소켓 버퍼의 형태로 만들어 준다. 즉, 커널에서는 보내고자 하는 데이터를 소켓 버퍼의 형태로 만들어주며, 넘겨받는 것은 소켓 버퍼라는 것을 가정할 수 있게 된다.
 
    네트워크 디바이스 드라이버의 전체적인 프로그램 구조는 아래와 같으며 이 구조를 대부분이 사용하고 있으므로 이것을 위주로 해서 검증해 보면 된다.
 
      ①. Module의 load - 디바이스 드라이버를 모듈로 로딩한다.
      ②. Device의 검출 - PCI 장치를 검출하며 네트워크 디바이스 드라이버를 커널에 등록한다.
      ③. Device driver의 초기화 - DMA frame buffer를 초기화하고 멀티캐스팅 어드레스 저장 공간에 대해서 초기화를 한다.
      ④. Device의 open - 디바이스를 사용하기 위해서 open을 하며 디바이스 드라이버의 초기화 부분을 이곳에서 호출한다.
      ⑤. Device의 ioctl - IOCTL에 대한 것을 정의한다.
      ⑥. Device의 close - 디바이스의 사용을 끝내고, IRQ번호의 해지와 DMA를 위해서 할당한 buffer를 해지한다.
      ⑦. Data transmission(데이터의 전송) - DMA buffer상에 보내고자 하는 데이터를 올려놓고, DMA control를 설정한다.
      ⑧. Interrupt의 처리 - Transmission의 처리, receive의 처리를 담당하는 것으로 데이터의 전송이 끝이 났거나 혹은 새로운 데이터가 들어왔을 때에 발생하는 인터럽트를 처리하는 것을 목적으로 한다.
      ⑨. Multicast address의 처리 - 멀티캐스트 어드레스를 관리하는 레지스터에 대한 처리를 담당한다.
      ⑩. Device의 software reset - 하드웨어적으로 이상이 있거나 혹은 디바이스의 제어를 목적으로 소프트웨어적으로 리셋을 한다. 이것은 특히 하드웨어가 돌이킬 수 없는 상황에 빠질 수도 있으므로 대체적으로 구현을 하는 것이 필요하다.
      ⑪. Module의 unload - 사용이 끝난 모듈을 메모리에서 제거한다.
 
 
    즉, 위에서 정의한 것들에 대해서 entry를 가지고 있어야 하며 이러한 entry는 커널에 의해서 사용하게 되므로 인터페이스가 정의된 방법을 따라야 한다. 물론 위에서 말하는 것들에 대해서 전부가 필요한 것은 아니며 Network device driver에서는 다음과 같은 것을 정의 하도록 하고 있다.
 
    드라이버를 설치한 후에는 네트워크 상황에 따라 적절한 설정이 필요한데, 여기에는 IP 주소의 할당 및 network mask, broadcast address를 주며, routing table에 등록 시켜야 한다. 이와 같은 일을 하기위해서는 ifconfig와 route라는 명령어를 사용하여 다음과 같이 설정을 하게 된다.
 
   
  shell> ifconfig eth0 210.189.178.2 netmask 255.255.255.0
  hell> route add -net 210.189.178.0 netmask 255.255.255.0 dev eth0
 
 
 
    위와 같은 설정에서 eth0는 디바이스에 대한 인터페이스 이름이 되며 이너넷 카드가 둘 이상이 존재할 경우에는 eth0, eth1, eth2, .. 순으로 운영체제가 이더넷 장치에 이름을 할당한다. ifconfig 명령을 사용해서 현재 설치된 network device들의 이름을 확인할 수 있을 것이다.
 
 
    참고로 네트워크 디바이스 드라이버와 관련 된 구조체에 대해 알아보겠다. 네트워크 디바이스만을 위한 net_device 구조체는 /include/linux/netdevice.h 에 정의 되어 있으며 다음과 같다.
 
      + 표 7-3-4. net_device 구조체의 내용
   
필 드 내 용
char name[IFNAMSIZ]  네트워크 디바이스의 인터페이스 이름
unsigned long rmem_end  공유된 받기를 위한 메모리의 끝 주소
unsigned long rmem_start  공유된 받기를 위한 메모리의 시작 주소
unsigned long mem_end  공유된 메모리의 끝 주소
unsigned long mem_start  공유된 메모리의 시작 주소\
unsigned long base_addr  디바이스의 I/O를 위한 주소
unsigned int irq  디바이스에 할당된 IRQ(인터럽트) 번호
unsigned char if_port  인터페이스를 위한 포트 타입(BNC, AUI, TP, etc.)
unsigned char dma  DMA 채널 번호(DMA master로 동작하는 PCI 카드는 해당 없음)
unsigned long state  디바이스의 현재 상태
struct net_device *next  네트워크 디바이스들의 연결 리스트
int (*init)(struct net_device *dev)  네트워크 디바이스의 초기화 함수에 대한 포인터(한번만 호출됨 - register_netdevice() 함수에서 호출함)
struct net_device *next_sched  네트워크 디바이스 구조체들의 다음 스케줄링 연결 포인터
int ifindex  인터페이스의 index
int iflink  인터페이스의 링크
struct net_device_stats * (*get_stats)(struct net_device *dev)   디바이스의 statistics(통계정보)알려주는 함수에 대한 포인터.
struct iw_statistics * (*get_wireless_stats)(struct net_device *dev)  디바이스의 wireless statistics 알려주는 함수에 대한 포인터
unsigned long trans_start  마지막으로 Tx를 한 시점으로 jiffies값을 가진다.
unsigned long last_rx  마지막으로 Rx를 한 시점을 나타낸다.
unsigned short flags  인터페이스의 flag이다.
unsigned short gflags   
unsigned short mtu  최대 전송 단위를 byte 단위로 나타냄
unsigned short type  인터페이스 하드웨어 타입
unsigned short hard_header_len  하드웨어 헤더의 길이
void *priv  디바이스 드라이버의 private data에 대한포인터( file object의 priv와 같은 역할을 할 수 있다.)
struct net_device *master  이 디바이스가 속해있는 그룹의 master 디바이스에 대한 포인터
unsigned char broadcast[MAX_ADDR_LEN]  하드웨어 broadcast 주소
unsigned char pad  dev_addr이 8bytes의 단위로 정렬되도록 만든다.
unsigned char dev_addr[MAX_ADDR_LEN]  하드웨어 주소
unsigned char addr_len  하드웨어 주소의 길이
struct dev_mc_list *mc_list  Multicast MAC 주소에 대한 포인터
int mc_count  현재 가지고 있는 multicast의 수를 유지한다.
int promiscuity  Promiscous mode를 표시(네트워크 상의 모든 패킷을 다 받는다.)
int allmulti  모든 multicast되는 패킷을 다 받는다.
int watchdog_timeo  Watch dog timer의 timeout 설정
struct timer_list  Watchdog_timer의 리스트 연결
void *atalk_ptr  Apple Talk link (이하의 4개 필드는 protocol에 의존한다.
void *ip_ptr  IPv4에 특수한 데이터의 포인터
void *dn_ptr  DECnet에 특수한 데이터의 포인터
void *ipv6_ptr  IPv6에 특수한 데이터의 포인터
void *ec_ptr  Econet에 특수한 데이터의 포인터
struct Qdisc *qdisc  네트워크 디바이스 구조체와 관련된 Qdisc 구조체는 패킷의 보내기와 받기를 위한 패킷의 큐를 말하는 것으로 해당 디바이스 드라이버와 연결 되어있다. 패킷을 보내거나 받기를 원하면 이곳에 패킷을 두고, 스캐줄링을 요청하면 해당 디바이스 드라이버나 상위의 프로토콜이 처리해 줄 것이다.
struct Qdisc *qdisc_sleeping  현재 sleeping상태에 있는 Qdisc구조체
struct Qdisc *qdisc_list  Qdisk의 리스트에 대한 포인터
struct Qdisc *qdisc_ingress  Qdisk의 진입점에 대한 포인터s
unsigned long tx_queue_len  Tx를 위한 패킷의 queue에 최대로 허용된 frame들의 수
spinlock_t xmit_lock  hard_start_xmit()함수에서 사용할 lock
int xmit_lock_owner  hard_start_xmit()함수를 호출하고 있는 CPU의 ID
spinlock_t queue_lock  디바이스의 queue lock
atomic_t refcnt  디바이스에 대한reference count
int deadbeaf  디바이스가 현재 등록되지는 않았으나, 사용자가 있음을 말해주는 flag이다.
int features  네트워크 디바이스의 특성(feature)을 표시한다.
void (*uninit)(struct net_device *dev)  네트워크로부터 detache된 후에 불려지는 함수로 init()함수와 반대되는 일을 한다. (unregister_netdevice()에서 호출함)
void (*destructor)(struct net_device *dev)  마지막 사용자의 reference가 사라지고난 후에 호출된다.
int (*open)(struct net_device *dev)  디바이스의 open() 함수
int (*stop)(struct net_device *dev)  디바이스의 stop() 함수
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)  하드웨어 헤더를 생성하는 함수
void (*set_multicast_list)(struct net_device *dev)  멀티 캐스팅이 지원되는 경우에 멀티 캐스트 리스트를 생성하기 위해서 호출됨
int (*set_mac_address)(struct net_device *dev, void *addr)  하드웨어의 MAC 주소를 변경하고자 할 때 호출됨. 참고로 Ethernet의 경우는 고정된 하드웨어 주소를 사용하는 경우가 많음
int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd)  디바이스 드라이버에 대한 ioctl() 메쏘드 함수
int (*set_config)(struct net_device *dev, struct ifmap *map)  하드웨어 설정을 바꾸기 위해서 호출됨
int (*hard_header_cache)(struct neighbour *neigh  하드웨어에서 유지하는 이웃에 있는 하드웨어 주소를 관리하는 함수에 대한 포인터
void (*header_cache_update)(struct hh_cache *bh, struct net_device *dev, unsigned char *haddr)  하드웨어에서 유지하는 이웃에 있는 하드웨어 주소를 업데이트 해주는 함수에 대한 포인터
int (*change_mtu)(struct net_device *dev, int new_mtu)  경로(Path)에서 지원하는 MTU(Maximum Transfer Unit)를 변경하는 함수
void (*tx_timeout)(struct net_device *dev)  Tx timeout이 될 경우에 호출되는 함수에 대한 포인터
int (*hard_header_parse)(struct sk_buff *skb, unsigned char *haddr)  하드웨어 주소를 분석(parse)하기 위해서 사용되는 함수에 대한 포인터
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *)  이웃(neighbour)에 대한 정보를 구성하기 위해서 호출되는 함수에 대한 포인터
int (*accept_fastpath)(struct net_device *, struct dst_entry *)  하드웨어에서 지원하는 목적지 엔트리(destination entry)를 찾기 위해서 호출되는 함수에 대한 포인터
struct module *owner  모듈의 사용자를 표시함
struct net_bridge_port *br_port  네트워크 bridge의 port를 나타냄
rwlock_t fastpath_lock  Fast path에서 사용되는 lock
struct dst_entry *fastpath[NETDEV_FASTROUTE_HMASK + 1]  Fast path에서 사용되는 목적지 엔트리의 배열에 대한 포인터
struct divert_blk *divert  Ethernet frame을 다른 곳으로 보내기 위해서 사용되는 divert_blk 구조체에 대한 포인터로서 TCP 및 UDP에 대한 목적지(destination)와 시작지(source)에 대한 port 번호와 프로토콜 번호를 가지는 필드로 구성되어 있다.
 
 
    이곳에서 정의된 모든 필드들을 다 사용하는 것은 아니다. 디바이스가 지원하고자 하는 기능을 위해서 필요한 필드만을 설정 하면 된다.
 
    net_device 구조체의 feature필드를 차지하는 값은 /include/linux/device.h에 아래와 같이 정의된다.
 
      + 표 7-3-5. feature 필드 매크로 내용
   
매크로 설 명
NETIF_F_SG 1  Scatter/gather I/O를 지원하는 네트워크 디바이스이다.
NETIF_F_IP_CSUM 2  IPv4상에서 TCP/UPD만이 checksum이 가능하다.
NETIF_F_NO_CSUM 4  Checksum이 필요하지 않다.
NETIF_F_HW_CSUM 8  모든 패킷에 대해서 하드웨어적인 checksum이 가능하다.
NETIF_F_DYNALLOC 16  스스로가 할당과 해지가 가능한 디바이스이다.
NETIF_F_HIGHDMA 32  High memory에 대한 DMA가 가능하다.
NETIF_F_FRAGLIST 1  NETIF_F_SG와 동일함
 
 
    통계정보를 가지는 net_device_stats 구조체는 include/linux/netdevic.h에 아래와 같은 필드로 구성되어 있다.
 
      + 표 7-3-6. net_device_stats 구조체의 내용
   
매크로 설 명
unsigned long rx_packets  받은 패킷의 수
unsigned long tx_packets  전달한 패킷의 수
unsigned long rx_bytes  받은 패킷의 byte수
unsigned long tx_bytes  전달한 패킷의 bytes수
unsigned long rx_errors  받은 패킷의 에러 수
unsigned long tx_errors  전달한 패킷의 에러 수
unsigned long rx_dropped  받은 패킷에서 drop된 수
unsigned long tx_dropped  전달한 패킷에서 drop된 수
unsigned long multicast  멀티 캐스트 된 패킷의 수
unsigned long collisions  패킷에서 충돌(collision)이 발생한 수
unsigned long rx_length_errors  받은 패킷의 에러 중 길이에 에러가 있는 수
unsigned long rx_over_errors  받은 패킷이 receive buffer에서 overflow를 발생한 수
unsigned long rx_crc_errors  받은 패킷에서 CRC(Cyclic Redundancy Error)가 발생한 수
unsigned long rx_frame_errors  받은 frame이 align이 안된 에러가 발생한 수
unsigned long rx_fifo_errors  받은 패킷이 FIFO overrun을 일으킨 수
unsigned long rx_missed_errors  받지 못한 패킷의 수
unsigned long tx_aborted errors  보내는 부분에서 abort된 패킷의 수
unsigned long tx_carrier_errors  보내는 부분에서 carrier가 없어서 발생한 error의 수
unsigned long tx_heartbeat_errors  보내는 부분에서 생긴 heart beat 에러의 수
unsigned long tx_window_errors  보내는 곳에서 window상에서 에러가 발생한 수
unsigned long rx_compressed  받은 패킷이 압축된 수
unsigned long tx_compressed  보내는 패킷이 압축된 수
 

반응형

'리눅스' 카테고리의 다른 글

PCI 소개  (0) 2010.09.17
vi, vim 명령어  (0) 2010.05.31
Snull 디바이스 드라이버  (0) 2010.05.18
디바이스 드라이버 인터럽트 처리  (0) 2010.05.18
gcc 다운그레이드  (0) 2010.05.01
Posted by pmj0403