[Linux] Network Device Driver  +   [Desktop/Linux]   |  2015.04.23 17:01

출처: http://crazyhoon.tistory.com/105



1.1.            Network Device Driver

리눅스에서의 network device driver data 수신과 전송을 담당한다실제적인 network devicecontrol하며상위 protocol layer에서 내려오는 data 하위의 physical layer 전달해 준다또한하위의 physical layer에서 올라오는 data protocol layer에서 인식할  있도록 적절한 연산을 하는 기능도 들어가 있다.

Ethernet device 경우에는 device 검출 시에 ~/drivers/net/net_init.c 있는 init_etherdev() 호출하게 되고다시  함수는 init_netdev() 함수를 호출하게 되어 network device 등록된다등록은register_netdev() 함수가 맡고 있다함수를 호출할 미리 device driver에서 고유하게 사용할 data구조를 parameter값으로 넘겨줌으로써나중에 device driver routine 호출될  구조체에 담긴 정보를 토대로 연산을 수행하게 된다이러한 구조체의 값으로 사용될  있는 것으로는 Buffer들에 대한description device driver 고유한 정보가 있겠다.

 

자 그럼 이젠 kernel device driver간의 interface에 대해서 살펴보도록 하자. Kernel은 명확히 정의된interface를 통해서 device에 접근을 시도하게 되며따라서 device driver에서는 이러한 kernel이 알고 있는 interface를 제공하여야만 한다우리가 만들려고 하는 network device driver에 대한 interface로는 다음과 같은 것들이 있다이것을 기초로 해서 device driver의 기본 구성이 만들어 진다.

 

l  Open – network device에 대해서 open을 하게 된다이곳에서 대부분의 hardware initialization이 일어나게 된다.

l  hard_start_xmit – data의 전송요구가 있을 경우에 호출되며, data bus상에 놓기 위한 기본적인 일을 행한다.

l  stop – device에 대한 더 이상의 요구가 없을 때 kernel에 의해서 호출된다. Device close하는 시점이 될 수 있을 것이다. Close를 하기 전에 사용하고 있던 resource에 대한 것들을release한다.

l  get_stats – device에 대한 statistics 정보를 구하기 위해서 kernel에 의해 호출된다. Network device driver는 당연히 이것과 관련된 정보를 수집해 주어야 할 것이다.

l  set_multicast_list – multicast address setting하기 위해서 kernel에 의해 호출되며우리가 만들고자 하는 device에는 이것을 위한 register를 가지고 있다.[1] , device driver에서 이곳에 multicast address를 보관하거나 새로이 update하는 연산을 해주게 된다.

l  do_ioctl – ioctl에 대한 요구를 처리하는 부분으로 우리가 만들고자 하는 network device driver에서는 별다른 일을 해주지 않는다다만 function entry는 채워주어야 한다.

그림 54.Network Device Driver의 기본 구조

[그림2] network device driver의 기본구조를 보여준다여기서 중요한 것은 이러한 device driver에 대한 interface routine들은 전부 device structure에 이것을 가르키는 pointer를 저장해서 kernel에 알려주는 것으로 kernel은 새로운 network device가 있다는 것을 알게 되며나중에 kernel network device driver에 대한 요구가 있을 때, device structure를 찾아서 해당하는 function access하게 된다.

1.1.1.   Ethernet에 대한 이해

Ethernet이란 1970년대에 Xerox에서 개발한 근거리의 packet교환 network 말하는 것으로, IEEE 802.3 표준으로 정해져 있다현재는 가장 인기 있는 LAN 상에서 사용하는 기술이며, 10Mbps100Mbps data 전송한다. CSMA/CD(Carries Sense Multiple Access/Collision Detection)방식으로 작동하는데이것은 전송미디어에 다른 host에서 packet 보내고 있을 경우 충돌(collision) 있음을 보내고자 하는 host측에서 알게 되며얼마간의 간격을 두고 다시 보내려는 시도를 하게 된다는 것이다따라서전송속도가 100Mbps라고 하더라도 traffic 많을 경우에는 이러한 속도가 나오지 못한다.

보통이 LAN환경에서는 Ethernet card를 이용해서 network을 구성한다. Ethernet에서는 주소를 지정하는 방법으로 6byte card에 고유한 번호를 사용한다이렇게 할당된 address IP address와 함께, network하에서 system을 찾을 수 있도록 한다. [그림8]은 Ethernet packet의 구성을 보여준다또한TCP/IP ethernet에서의 encapsulation에 대해서도 보여준다, TCP/IP로부터 넘겨받은 packet data를 다시 Ethernet packet으로 만들어서 network에 날려 보내게 된다.

 

그림 55. Ethernet Frame Structure

[그림8] 보면보내고자 하는 상위의 protocol data packet들은 frame data부분에 들어가서 전송되며이것은 physical media 성질에 의해서 상위의 protocol에는 상관없이 data 보내고 받기 위한것이다.

Ethernet address network card vendor에 고유하게 할당되어 사용되며, ARP/RARP protocol에 의해서 IP address mapping되어 사용된다. 이러한 ethernet address중 특정의 목적으로 사용되는address가 있는데 이러한 것으로는 multicast address broadcast address 등이 있다.

Multicast address는 주어진 multicast address를 허락한 network내의 몇몇 컴퓨터들에게만 전달되는 것이며이러한 multicast에 참여한 host들을 통틀어서, multicast group이라고 한다. Multicast group에 참여하기 위해서는 먼저 자신의 host에게 network interface에서 multicast address를 허락하라는 명령을 보내야 하며, Ethernet card에서는 이것을 자신에게 보내지는 모든 packet destination address와 비교하여자신에게 전송되는 packet인가를 찾게 되는 것이다. Broadcast address는 모든network내의 host들이 다 packet을 받아보도록 하는 주소로서 사용되며이것은 network traffic을 증가시키게 할 수 있다.

1.1.2.   Protocol이란?

Protocol이란 서로간에 합의한 통신 방법이다이러한  중에서 가장 많이 사용되는 것으로는 TCP/IP 있으며, [그림8]에서와 같이 TCP/IP ethernet위에 올라가게 된다. Kernel 이러한 TCP/IP protocol 구현하고 있다물론 TCP/IP 반드시 ethernet위에만 올라가야 한다는 것은 아니다. TCP 연결 지향적인 service 제공하며이와는 다른 UDP 비연결적인 service 제공하기 위해서 존재한다. IP 이러한 두개의 protocol계층에서 넘겨받은 data 상대편으로 정확히 전달되는 것을 보장하는 역할을 하게 된다. Protocol 실제적인 구현은 process queue 되어 있는 경우가 많다날아오는 packet이나 보내고자 하는 packet 있을 경우이것은 하나의 실행중인 process 대한 input queue 들어가게 되며이곳에서 process 처리를 기다려하위나 상위의 protocol data unit으로queue 전달 되게 된다.

 

그림 56.Protocol Stack interface

[그림 9] TCP/IP상에서의 protocol stack application interface 보여주고 있다. TCP/IP 상위에는 application과의 interface 제공하기 위해서 socket 정의하고 있으며이것은 BSD UNIX상에서 먼저 구현되었으며다른 system에서도 채택되어 현재는 일반적인 이야기가 되었다이것에 대해서는 조금 후에 좀더 살펴보도록 한다.

1.1.3.   Socket이란그리고, socket buffer?

Socket이란 API로서 TCP/IP로의 연결통로를 제공한다고 생각하면 된다, socket을 통해서application data network을 통해서 목적지로 전송할 수 있다이것은 주로 library로 제공되며, program compile하게 될 때 link된다여기서는 socket programming에 대한 것은 생략하도록 했다.이것은 너무나 많은 책들에서 다루고 있기에 그것들을 참고하기 바란다.

Socket에서 사용하는 data들은 socket buffer라는 형태로 만들어져서 linux에서 제공하는 protocol stack의 처리를 받게 되며최종적으로 network device driver에 도달하게 된다따라서, network device driver writer socket buffer에 대한 것만 생각하면 된다나중에 data을 상위의 protocol stack으로 전송하게 될 때도 사용하게 되는 것이 바로 이러한 socket buffer이다.

먼저 socket buffer에 대한 field중 다음과 같은 것에 대해서 알아보자.

 

l  struct device *dev – socket buffer가 전달되거나 보내진 device를 가르킨다.

l  __u32 saddr, __u32 daddr, __u32 raddr – IP protocol에 의해서 사용되며, source, destination, router의 주소를 가르킨다.

l  unsigned char *head,  unsigned char *data, unsigned char *tail, unsigned char *end – network을 통해서 전달되는 packet data를 가르키는 부분이다.

l  unsigned long len – data 자체의 길이를 나타내는 것으로 skb->tail – skb->head를 가진다.

l  unsigned char ip_summed – TCP/IP에 의해서 checksum을 하는데 사용된다이것은receive packet에 대해서 device driver에 의해서 set된다.

l  unsigned char pkt_type – PACKET_HOST, PACKET_BROADCAST, PACKET_MULTICAST, 혹은 PACKET_OTHERHOST등의 값으로 driver에 의해서 set된다받은 packet host로 오는 것인지, broadcast된 것인지혹은 multicast 된 것인지를 나타낸다하지만, ethernet driver의 경우에는 eth_type_trans가 이 역할을 해주므로 따로 나타내어줄 필요는 없다.

l  Union { unsigned char *raw; […] } mac – packet receive할 때 set되며 packet을 처리하는데 사용된다위에서 언급한 것과 마찬가지로 eth_type_trans가 처리를 해주므로 ethernet에서는 다룰 필요가 없다.

 

이것 이외에도 여러 개의 field socket buffer에 있지만 관심을 가질 필요는 없다이것은 우리가 다루어 주지 않아도 상관없다.

Socket buffer에서 사용하게 되는 function으로는 다음과 같은 것이 있다.

 

l  struct sk_buff *alloc_skb( unsigned int len, int priority ); - socket buffer를 할당한다.

l  struct sk_buff *dev_alloc_skb( unsigned int len ); - socket bufer를 할당하며, priorityGFP_ATOMIC을 주고, head tail사이에 16bytes를 남겨둔다.

l  void kfree_skb( struct sk_buff *skb, int rw ); - 할당된 socket buffer free한다.

l  void dev_kfree_skb( struct sk_buff *skb, int rw ); - 할당된 socket buffer free하며, bufferlocking을 정확히 처리한다. Driver에서는 dev_kfree_skb()를 사용해서 socket buffer free해주어야 한다.

l  unsigned char *skb_put( struct sk_buff *skb, int len ); - buffer의 끝에 data를 넣고, taillen field를 고친다.

l  unsigned char *skb_push( struct sk_buff *skb, int len ); - socket buffer의 앞에서부터 data를 채워넣는다.

l  int skb_tailroom( struct sk_buff *skb ); - socket buffer에 남은 data를 위한 space의 크기를return한다.

l  int skb_headroom( struct sk_buff *skb ); - data앞에 남은 부분의 사용 가능한 space의 크기를 return한다.

l  void skb_reserve( struct sk_buff *skb, int len ); - socket buffer에 데이터를 쓰기 전에headroom space를 마련한다.

l  unsigned char * skb_pull( struct sk_buff *skb, int len ); - packet head로부터 data를 떼어낸다.

 

위와 같은 함수들을 사용해서 kernel로부터 넘겨받은 socket buffer에 대한 연산을 하며또한 받은packet에 대해서 kernel로 넘겨주기 전에 socket buffer의 형태로 만들어 준다, kernel에서는 보내고자 하는 data socket buffer의 형태로 만들어주며넘겨 받는 것은 socket buffer라는 것을 가정할 수 있게 된다.[2][그림 10] socket buffer가 어떻게 구현되어 있는지를 보여주고 있다.

그림 57.Socket Buffer Structure

[그림10]에서 보듯이 socket buffer head tail data 삽입할  있기에, packet header 붙이거나 tail CRC(Cycle Redundancy Check)[3] 같은 checksum 붙이게   사용하기 편리하다참고로 이것은 SCO UNIX 경우 stream driver 구현하는 것과 유사하다.

1.1.4.     전체적인 구조.

네트워크 디바이스 드라이버의 전체적인 프로그램 구조는 아래와 같다이 구조를 대부분이 사용하고 있으므로 이것을 위주로 해서 검증해 보면 될 것이다.

 

1.     Module load – device driver module loading한다.

2.     Device의 검출 – PCI device의 검출하며, network device driver kernel에 등록한다.

3.     Device driver의 초기화 – DMA frame buffer의 초기화하고 multicasting address저장 공간에 대해서 초기화를 한다.

4.     Device open – Device를 사용하기 위해서 open을 한다. Device driver의 초기화 부분을 이곳에서 호출한다.

5.     Device ioctl – IOCTL에 대한 것을 정해해서 둔다. (우리가 만들고자 하는 driver에서는 특별히 사용하고 있지 않지만 entry로서 가진다.

6.     Device close – Device의 사용을 끝내고, IRQ번호의 release DMA를 위해서 할당한buffer free를 한다.

7.     Data transmission(데이터의 전송) – DMA buffer상에 보내고자 하는 data를 올려놓고, DMA control setting한다.

8.     Interrupt의 처리 – Transmission의 처리, receive의 처리를 담당하는 것으로 data의 전송이 끝이 났거나 혹은 새로운 데이터가 들어왔을 때에 발생하는 interrupt를 처리하는 것을 목적으로 한다.

9.     Multicast address의 처리 – 이것은 우리가 만들고자 하는 device에 고유한 것으로 multicast address를 관리하는 register에 대한 처리를 담당한다.

10.  Device software reset. – hardware적으로 이상이 있거나 혹은 device의 제어를 목적으로software적으로 reset을 해준다이것은 특히 hardware가 돌이킬 수 없는 상황에 빠질 수도 있으므로 대체적으로 구현을 하는 것이 필요하다.

11.  Module unload – 사용이 끝난 module을 메모리에서 제거한다.

 

위에서 정의한 것들에 대해서 entry를 가지고 있어야 하며이러한 entry kernel에 의해서 사용하게 되므로 interface가 정의된 방법을 따라야 한다물론 위에서 말하는 것들에 대해서 전부가 필요한 것은 아니다. Network device driver에서는 다음과 같은 것을 정의 하도록 하고 있다.

1.1.5.   Network Setting

드라이버를 설치한 후에는 Network 상황에 따라 적절한 setting 필요한데여기에는 IP address 할당  network mask, broadcast address 주어야 하며, routing table 등록 시켜야 한다이와 같은일을 하기위해서는 ifconfig route라는 명령어를 사용한다반드시 root권한을 가진 사용자만 해주어야한다다음과 같은 방법으로 해보자.

 

(ex)    ifconfig eth0 165.213.175.46

ifconfig eth0 broadcast 165.213.175.63

ifconfig eth0 netmask 255.255.255.192

        route add -net 165.213.175.0 netmask 255.255.255.192 dev eth0

        route add default gw 165.213.175.1

예제 3. Network setting

위의 예에서 eth0 device 대한 interface name 되며, Ethernet card  이상이 존재할 경우에는 eth0, eth1, eth2, .. 순으로 운영체제가 Ethernet device 이름을 할당한다따라서명령어를 실행하기에 앞서자신이 설치한 device 이름을 확인하는 것이 필요하다 경우ifconfig 명령을 사용해서 현재 설치된 network device들의 이름을 확인할  있을 것이다.

위의 예제는 참고로서 사용하기 바라며자신의 network상황에 대해서  이해한 후에 하기 바란다. Driver module 제거하기 전에 반드시 다음과 같은 명령어로  이상  network interface 사용하지않는다는 것을 알려 주어야 한다주의하기 바란다.

 

ex)#ifconfig eth0 down

1.1.6.         네트워크 디바이스 구조체

네트워크 디바이스 드라이버를 작성하기 위해서는 먼저네트워크 디바이스만을 위한 net_device구조체에 대해서 이해해야 한다. ~/include/linux/netdevice.h를 보도록 하자.

 

Field

Description

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구조체[4]의 포인터

struct Qdisc *qdisc_sleeping

현재 sleeping상태에 있는 Qdisc구조체

struct Qdisc *qdisc_list

Qdisk의 리스트에 대한 포인터

struct Qdisc *qdisc_ingress

Qdisk의 진입점에 대한 포인터

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구조체에 대한 포인터로서 TCPUDP에 대한 목적지(destination)와 시작지(source)에 대한 port번호와 프로토콜 번호를 가지는 필드로 구성되어 있다.

 46. net_device구조체의 정의

이곳에서 정의된 모든 필드들을 다 사용하는 것은 아니다디바이스가 지원하고자 하는 기능을 위해서 필요한 필드만을 설정해 주면 될 것이다.

 

net_device구조체의 feature필드를 차지하는 값은 아래와 같이 정의된다.

 

Feature

Value

Description

NETIF_F_SG

1

Scatter/gather I/O[5]를 지원하는 네트워크 디바이스이다.

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와 동일함

 47. net_device구조체의 feature필드의 

또한 통계정보를 가지는 net_device_stats구조체는 아래와 같은 필드로 구성된다.

 

Field

Description

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[6] overrun을 일으킨 수

unsigned long rx_missed_errors

받지 못한 패킷의 수

unsigned long tx_aborted errors

보내는 부분에서 abort된 패킷의 수

unsigned long tx_carrier_errors

보내는 부분에서 carrier[7]가 없어서 발생한 error의 수

unsigned long tx_heartbeat_errors

보내는 부분에서 생긴 heart beat[8] 에러의 수

unsigned long tx_window_errors

보내는 곳에서 window상에서 에러가 발생한 수

unsigned long rx_compressed

받은 패킷이 압축된 수

unsigned long tx_compressed

보내는 패킷이 압축된 수

 48. net_device_stats구조체의 정의

net_device_stats구조체는 패킷을 받거나 보낼 때발생되는 에러 정보 및 통계정보를 가지는 구조체로 인터럽트의 발생시에 관련된 레지스터를 살펴서어떤 에러가 발생했는지를 검출해야지만 고쳐줄 수 있는 부분이다.

통계정보는 Error혹은 디버깅(debugging) 정보를 상위의 application에 전달하는 것을 목적으로 사용된다이러한 정보는 디바이스 드라이버의 구조체[9]에 들어가 있기에네트워크 디바이스의 함수 호출에서 파라미터로 넘겨지는 net_device 구조체를 이용해서 접근이 가능하다이러한 정보를 응용 프로그램에서 가지고 오는 방법으로는 ioctl() 메쏘드를 이용해서 디바이스 드라이버에 직접적으로 요청하게 되며디바이스 드라이버는 이 요구에 대해서 적절하게 응답하도록 프로그램하면 된다물론 내부적인 명령(command)에 대한 code를 미리 정의해 두어야 할 것이다.

만약 현재 네트워크 디바이스 드라이버를 디버깅한다고 가정한다면 이와 같은 정보를 이용해서 현재network device driver가 제대로 작동하고 있는지를 가르쳐줄 목적으로도 사용할 수 있을 것이다이와 같은 정보는 ifconfig –a 명령어를 통해서 볼 수 있다[10].

1.1.7.         간단한 Network Device Driver 구현 

이번 장에서 볼 것은 간단한 네트워크 디바이스 드라이버의 구현이다하드웨어적인 것은 전부다 생략하기로 하자이것은 실제로 사용하는 chip에 대해서 자세한 이해를 필요로 한다따라서우리가 선택할 수 있는 디바이스 드라이버 예는 ~/drivers/net/dummy.c ~/drivers/net/loop.c가 된다. Dummy.c dummy 네트워크 디바이스 드라이버 역할을 하는 드라이버이다아무런 동작도 하지 않는 단순히 네트워크 디바이스 드라이버로 존재만 하는 코드이다. Loop.c loopback 인터페이스를 구현하기 위해서 사용되는 네트워크 디바이스 드라이버이다이 디바이스 드라이버로 데이터를 보내면,즉시 자신에게 데이터가 도착한 것처럼 보이게 될 것이다먼저 dummy.c부터 보기로 하자.

 

static struct net_device dev_dummy;

static int __init dummy_init_module(void)

{

             int err;

 

             dev_dummy.init = dummy_init;            /* init()함수의 등록 */

             SET_MODULE_OWNER(&dev_dummy);

 

             /* Find a name for this unit */

             err=dev_alloc_name(&dev_dummy,"dummy%d");

             if(err<0)

                           return err;

             if (register_netdev(&dev_dummy) != 0)

                           return -EIO;

             return 0;

}

 

static void __exit dummy_cleanup_module(void)

{

             unregister_netdev(&dev_dummy);

             kfree(dev_dummy.priv);

 

             memset(&dev_dummy, 0, sizeof(dev_dummy));

             dev_dummy.init = dummy_init;

}

 

module_init(dummy_init_module);

module_exit(dummy_cleanup_module);

코드 366드라이버의 적재와 해제

모듈로 적재되는 것가 해제되는 부분이다이미 앞에서 보았을 것이다. dummy_init_module()함수에서net_device로 선언된 dev_dummy.init dummy_init함수를 넣고모듈의 사용자가 누군지를 표시한다(SET_MODULE_OWNER())[11]이젠 이 모듈에 대한 이름을 할당 받도록 한다(dev_alloc_name()).

dev_alloc_name()함수는 ~/net/core/dev.c에 정의된 함수로 네트워크 디바이스의 연결 리스트를 검사해서 같은 이름이 있는지를 확인하는 함수이다없다면넘겨받은 net_device 구조체에 이름을 넣는다.이것을 마치고 나면이젠 네트워크 디바이스로 등록하는 register_netdev()함수를 호출한다.

모듈의 해제에서는 네트워크 디바이스로 등록된 것을 해제하는 역할을 하는 unregister_netdev()함수를 호출한다그리고, dev_dummy.priv dummy 네트워크 디바이스로 할당된 메모리가 있다면 이것을 해제한다(kfree()). 마지막으로 dummy 네트워크 디바이스 구조체를 0으로 설정하고(memset()), 다시 초기화 함수를 dummy_init()로 설정한 다음 복귀한다.

 

네트워크 디바이스 드라이버의 등록과 해제는 register_netdev() unregister_netdev()함수가 맡고 있다이 함수들의 정의는 ~/drivers/net/net_init.c에 나와있으며 아래와 같다.

 

static struct net_device dev_dummy;

int register_netdev(struct net_device *dev)

{

             int err;

 

             rtnl_lock();

             if (strchr(dev->name, '%'))

             {

                           err = -EBUSY;

                           if(dev_alloc_name(dev, dev->name)<0)

                                        goto out;

             }

             if (dev->name[0]==0 || dev->name[0]==' ')

             {

                           err = -EBUSY;

                           if(dev_alloc_name(dev, "eth%d")<0)

                                        goto out;

             }

             err = -EIO;

             if (register_netdevice(dev))

                           goto out;

             err = 0;

out:

             rtnl_unlock();

             return err;

}

void unregister_netdev(struct net_device *dev)

{

             rtnl_lock();

             unregister_netdevice(dev);

             rtnl_unlock();

}

코드 367네트워크 디바이스 드라이버의 등록과 해제

rtnl_lock() routing table netlink에서 사용되는 세마포어를 획득하는 매크로이다. strchr()함수는 넘겨받은 두 파라미터에서 두번째 파라미터로 시작하는 첫번째 파라미터의 문자부분을 찾는다만약 있다면, dev_alloc_name()을 호출해서 새로운 네트워크 디바이스의 이름을 할당받는다만약 네트워크 디바이스의 이름이 0이나 공백(space)로 시작한다면 eth%d로 시작하는 이름을 할당하려고 할 것이다. %d에는 번호가 들어갈 것이다마지막으로 register_netdevice()함수를 호출해서 네트워크 디바이스 이름을 등록할 것이다.

해제하는 것은 unregister_netdev()함수이며단순히 등록을 해제하는 unregister_netdevice() 함수를 호출하는 일을 한다.

register_netdevice()함수는 네트워크 디바이스 구조체의 queue xmit에 대한 lock을 초기화 하고,네트워크 디바이스 드라이버의 init()함수를 호출해주며, rebuild_header()함수가 NULL인 경우에는 이를 default_rebuild_header()함수로 초기화 시켜주는 등의 일을 한다. unregister_netdevice()함수는 네트워크 디바이스의 close()함수를 호출해주며네트워크 디바이스들의 연결 리스트에서 디바이스를 나타내는 구조체를 제거하고디바이스 드라이버가 uninit()함수를 가진 경우에는 이 함수도 호출해 준다마지막으로 해당 네트워크 디바이스 구조체를 버리는 dev_put()함수를 호출한다[12].

 

dummy 네트워크 디바이스 드라이버의 등록시에 init()함수로 호출되는 것은모듈의 적재시에 등록된dummy_init() 함수이며아래와 같다.

 

static int __init dummy_init(struct net_device *dev)

{

             /* Initialize the device structure. */

             dev->hard_start_xmit       = dummy_xmit;

             dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);

             if (dev->priv == NULL)

                           return -ENOMEM;

             memset(dev->priv, 0, sizeof(struct net_device_stats));

             dev->get_stats   = dummy_get_stats;

             dev->set_multicast_list = set_multicast_list;

             /* Fill in the fields of the device structure with ethernet-generic values. */

             ether_setup(dev);

             dev->tx_queue_len = 0;

             dev->flags |= IFF_NOARP;

             dev->flags &= ~IFF_MULTICAST;

#ifdef CONFIG_NET_FASTROUTE

             dev->accept_fastpath = dummy_accept_fastpath;

#endif

             return 0;

}

코드 368. dummy_init()함수

이 함수가 해주는 일은 net_device 구조체를 초기화 시켜주는 일을 한다먼저 hard_start_xmit()dummy_xmit()를 가르키도록 하고, priv필드에는 네트워크 디바이스의 통계정보를 가질 공간을 할당한다만약 할당받은 메모리가 NULL이라면 ENOMEM을 에러값으로 돌려준다할당받은 메모리 공간은0으로 초기화하고(memset()), 통계정보를 구하는 get_stats에는 dummy_get_stats()함수를, multicast주소를 설정하는 set_multicast_list에는 set_multicast_list()함수를 넣어준다.

이외에 기본적인 net_device구조체의 초기화를 위해서 ether_setup()을 호출해주게 되는데이곳은 조금후에 다시 보도록 하자. Tx queue의 길이(tx_queue_len)은 0을 주고디바이스는 ARP를 사용하지 않는 다는 것을 나타내기 위해서 IFF_NOARP flag에 설정한다또한 다비아스가 multicasting도 지원하지 않으므로 IFF_MULTICAST flag에서 지운다만약 CONFIG_NET_FASTROUTE가 커널 컴파일에서 설정되었다면, accept_fastpath에는 dummy_accept_fastpath()를 넣는다.

 

net_device구조체의 flags필드에 들어갈 수 있는 값으로는 아래와 같은 것들이 있으며정의는~/include/linux/if.h에 나와 있다.

 

Flag

Value

Description

IFF_UP

0x0001

현재 네트워크 인터페이스가 UP상태이다(동작중).

IFF_BROADCAST

0x0002

Broadcasting을 지원한다.

IFF_DEBUG

0x0004

Debugging On시킨다.

IFF_LOOPBACK

0x0008

Loopback 인터페이스 이다.

IFF_POINTTOPOINT

0x0010

Point to point연결을 지원한다.

IFF_NOTRAILERS

0x0020

Trailer의 사용을 금한다.

IFF_RUNNING

0x0040

현재 네트워크 디바이스에 자원이 할당되어 있다.

IFF_NOARP

0x0080

ARP protocol을 사용하지 않는다.

IFF_PROMISC

0x0100

모든 네트워크 상의 패킷을 받겠다.

IFF_ALLMULTI

0x0200

모든 multicasting되는 패킷을 받겠다.

IFF_MASTER

0x0400

부하 분산자(load balancer) master로 동작한다.

IFF_SLAVE

0x0800

부하 분산자의 slave로 동작한다.

IFF_MULTICAST

0x1000

Multicasting기능을 지원한다.

IFF_VOLATILE

->

IFF_LOOPBACK | IFF_POINTPOINT | IFF_BROADCAST | IFF_MASTER | IFF_SLAVE | IFF_RUNNING

IFF_PORTSEL

0x2000

전송 media를 선택할 수 있다.

IFF_AUTOMEDIA

0x4000

전송 media를 자동으로 선택할 수 있는 기능이 있다.

IFF_DYNAMIC

0x8000

주소를 바꾸는 기능을 가진 dial-up 장치이다.

 49. net_device구조체의 flag

이러한 값들은 네트워크 디바이스의 현재 동작을 조절하는 목적으로도 사용되기 때문에 이 값들에 맞춰서 네트워크 디바이스 드라이버는 적절한 일을 해주어야 할 것이다만약 상위에서 IFF_PROMISC를 설정을 요청했다면디바이스 드라이버는 디바이스가 promiscuous모드를 지원하는지를 확인해서지원된다면 이러한 기능을 켜주어야(On) 할 것이다.

 

/* fake multicast ability */

static void set_multicast_list(struct net_device *dev)

{

}

 

#ifdef CONFIG_NET_FASTROUTE

static int dummy_accept_fastpath(struct net_device *dev, struct dst_entry *dst)

{

             return -1;

}

#endif

...

static int dummy_xmit(struct sk_buff *skb, struct net_device *dev)

{

             struct net_device_stats *stats;

 

             stats = (struct net_device_stats *)dev->priv;

             stats->tx_packets++;

             stats->tx_bytes+=skb->len;

 

             dev_kfree_skb(skb);

             return 0;

}

 

static struct net_device_stats *dummy_get_stats(struct net_device *dev)

{

             return dev->priv;

}

코드 369. dummy 네트워크 디바이스의 함수들

여기서 보이는 함수들은 전부 dummy 네트워크 디바이스 드라이버가 커널에 export하는 함수들이다. set_multicast_list() dummy_accept_fastpath()는 아무런 동작도 일으키지 않으며, dummy_get_stats()함수는 단순히 할당받았던 net_device구조체의 priv필드만을 돌려준다.

dummy_xmit()함수는 패킷의 전송을 위해서 호출되는 함수로 넘겨받는 것은 sk_buff와 해당net_device 구조체이다먼저 net_device구조체의 priv필드에서 통계정보에 대한 구조체를 가져오고,전달한 패킷의 수를 나타내는 tx_packets을 증가 시켜주며전달한 패킷의 byte수를 나타내는tx_bytes sk_buff의 길이 만큼을 더해준다패킷은 실제적인 전송이 일어나지는 않으므로 이곳에서 패킷을 버리는 dev_kfree_skb()함수를 호출한다.

이것으로 하나의 간단한 네트워크 디바이스 드라이버의 구현이 끝난다하지만실제적인 네트워크 디바이스 드라이버의 구현은 더 복잡하며인터럽트의 처리와 DMA의 구현등이 더 추가될 것이다.

 

이젠 앞에서 잠시 논의하다가 만 ether_setup()함수를 보기로 하자이 함수는 기본적인 net_device 구조체의 필드들에 대한 초기화를 수행하는 함수이다정의는 ~/drivers/net/net_init.c에서 찾을 수 있다.

 

void ether_setup(struct net_device *dev)

{

             dev->change_mtu            = eth_change_mtu;

             dev->hard_header           = eth_header;

             dev->rebuild_header       = eth_rebuild_header;

             dev->set_mac_address = eth_mac_addr;

             dev->hard_header_cache             = eth_header_cache;

             dev->header_cache_update= eth_header_cache_update;

             dev->hard_header_parse              = eth_header_parse;

             dev->type                        = ARPHRD_ETHER;

             dev->hard_header_len    = ETH_HLEN;

             dev->mtu                         = 1500; /* eth_mtu */

             dev->addr_len                 = ETH_ALEN;

             dev->tx_queue_len          = 100;    /* Ethernet wants good queues */            

             memset(dev->broadcast,0xFF, ETH_ALEN);

             /* New-style flags. */

             dev->flags                       = IFF_BROADCAST|IFF_MULTICAST;

             dev_init_buffers(dev);

}

코드 370. ether_setup()함수

ether_setup()함수는 Ethernet을 위한 net_device구조체를 기본적인 함수들과 상수값으로 채우는 일을 한다이곳에서 결정되는 필드들은 change_mtu, hard_header, rebuild_header, set_mac_address, hard_header_cache, heard_cache_update, hard_header_parse, type, hard_header_len(=ETH_HLEN[13]:14bytes), mtu(=1500bytes:ethernet 패킷의 최대 길이), addr_len(=ETH_ALEN:6bytes), tx_queue_len(=100),flags(=IFF_BROADCAST | IFF_MULTICAST)가 있다.

이것을 다 마쳤다면이젠 dev_init_buffers()함수를 호출해서 디바이스에 관련된 buffer를 초기화 한다현재는 아직 dev_init_buffers()함수에서 아무런 동작도 해주지 않는다.

 

지금까지 우리가 본것은 아무런 역할도 해주지 않는 가상의(dummy) 네트워크 디바이스 드라이버를 보았다이제부터 볼 것은 ifconfig a 명령에서 lo라고 나오는 loopback 네트워크 인터페이스를 만들기위한 디바이스 드라이버를 보도록 하자. Loopback 네트워크 인터페이스는 IP주소로 127.0.01이라는 특수한 주소를 할당받아서 사용하고 있다코드는 ~/drivers/net/loopback.c에 해당한다단순히 디바이스 드라이버의 상위 layer에서 받은 패킷을다시 상위의 프로토콜 layer로 돌려주는 역할을 한다.

 

#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)

...

/* Initialize the rest of the LOOPBACK device. */

int __init loopback_init(struct net_device *dev)

{

             dev->mtu                         = PAGE_SIZE - LOOPBACK_OVERHEAD;

             dev->hard_start_xmit       = loopback_xmit;

             dev->hard_header           = eth_header;

             dev->hard_header_cache             = eth_header_cache;

             dev->header_cache_update= eth_header_cache_update;

             dev->hard_header_len     = ETH_HLEN;                   /* 14  */

             dev->addr_len                 = ETH_ALEN;                   /* 6   */

             dev->tx_queue_len          = 0;

             dev->type                        = ARPHRD_LOOPBACK;    /* 0x0001                         */

             dev->rebuild_header       = eth_rebuild_header;

             dev->flags                       = IFF_LOOPBACK;

             dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);

             if (dev->priv == NULL)

                           return -ENOMEM;

             memset(dev->priv, 0, sizeof(struct net_device_stats));

             dev->get_stats = get_stats;

             if (num_physpages >= ((128*1024*1024)>>PAGE_SHIFT))

                           dev->mtu = 4096*4 - LOOPBACK_OVERHEAD;

             dev_init_buffers(dev);            /* ~/include/linux/netdevice.h에 정의 됨아무일도 안함 */

             return(0);

};

코드 371. loopback_init()함수

모든 네트워크 디바이스의 구조체(net_device구조체) dev_base의 연결된다이 변수는 loopback 디바이스를 나타내는 네트워크 디바이스 구조체로 초기화가 되며아래와 같이 ~/net/Space.c에 정의되어 있다.

 

struct net_device loopback_dev =

             {"lo", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, loopback_init};

struct net_device *dev_base = &loopback_dev;

코드 372. dev_base 정의

따라서, loopback디바이스는 lo를 인터페이스의 이름으로 가지며, loopback_init()가 초기화 함수로 디바이스의 등록에서 호출된다. NEXT_DEV Space.c에서 정의된 디바이스를 연결하기 위한 매크로 이다이렇게 등록된 디바이스는 ~/drivers/block/genhd.c에서 device_init()함수에서 CONFIG_NET으로 설정된 경우 net_dev_init()함수를 호출해서 초기화된다. device_init()함수를 불러주는 것은~/fs/partitions/check.c에 정의된 partition_setup()함수이며, __initcall() 매크로 선언에 의해서, ~/init/main.c에 정의된 do_initcalls()함수에서 __initcall_start __initcall_end사이에 있는 함수들을 호출하는 부분에서 실행 기회를 얻게 된다.

 

loopback_init()함수에서는 net_device구조체의 필드들을 초기화하는 역할을 하고 있다이곳에서 초기화 되는 정보로는 mtu(=PAGE_SIZE  LOOPBACK_OVERHEAD), hard_start_xmit(loopback_xmit), hard_header(=eth_header), hard_header_cache(=eth_header_cache), header_cache_update(=eth_header_cache_update), hard_header_len(=ETH_HLEN), addr_len(=ETH_ALEN), tx_queue_len(=0), type(=ARPHRD_LOOPBACK:0x0001), rebuild_header(=eth_rebuild_header), flags(=IFF_LOOPBACK), priv(=net_device_stats)가 있으며, get_stats get_stat()함수로 초기화 된다할당받은 priv필드의 메모리에 대해서는 0으로 초기화 되며현재 메모리의 물리적인 페이지의 수가 128 x 1024 x1024(=128Mbytes)를 페이지 크기로 나눈 수 보다 크다면, mtu 4096 x 4에서 LOOPBACK_OVERHEAD를 뺀 수로 초기화 한다이는 적어도 4페이지를 동시에 보내도록 하는 부분이다역시 dev_init_buffers()는 아무런 역할도 하지 않는 함수이다.

 

static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)

{

             struct net_device_stats *stats = (struct net_device_stats *)dev->priv;

 

/* 패킷을 보내는 부분이다. */

             if(atomic_read(&skb->users) != 1)

             {

                         struct sk_buff *skb2=skb;

                         skb=skb_clone(skb, GFP_ATOMIC);                        /* Clone the buffer */

                         if(skb==NULL) {

                                        kfree_skb(skb2);

                                        return 0;

                           }

                         kfree_skb(skb2);

             }

             else

                           skb_orphan(skb);

 

/* 이하의 부분은 패킷을 받는 것을 처리한다. */

             skb->protocol=eth_type_trans(skb,dev);

             skb->dev=dev;

#ifndef LOOPBACK_MUST_CHECKSUM

             skb->ip_summed = CHECKSUM_UNNECESSARY;

#endif

             netif_rx(skb);

             stats->rx_bytes+=skb->len;

             stats->tx_bytes+=skb->len;

             stats->rx_packets++;

             stats->tx_packets++;

             return(0);

}

 

static struct net_device_stats *get_stats(struct net_device *dev)

{

             return (struct net_device_stats *)dev->priv;

}

코드 373. loopback_xmit() get_stats()함수

loopback_xmit()함수는 초기화에서 hard_start_xmit필드로 정의되었기에 커널에서 패킷을 전달하기 위해서 호출될 것이다하는 일은 단순히 상위에서 받은 패킷을 다시 상위로 올려주는 역할을 한다마치 다른 곳에서 패킷을 받은 것과 같은 일을 해 줄 것이다.

먼저 네트워크 디바이스 드라이버 구조체에서 통계정보를 가지는 곳에 대한 주소를 얻는다(dev->priv). 소켓버퍼(sk_buff) 사용자 카운터를 읽어서(atomic_read()) 1이 아니라면새로운 소켓 버퍼를 하나 할당 받고동일한 소켓 버퍼를 하나 만든다이전에 있던 소켓 버퍼는 kfree_skb()함수를 호출해서 해제한다. skb_orphan()함수는 소켓 버퍼의 owner가 가진 destructor함수가 존재할 경우 이를 호출해주며소켓 버퍼의 owner를 없애주는 역할을 하는 함수이다.

소켓 버퍼의 protocol eth_type_trans()함수를 호출해서 초기화 한다. eth_type_trans()함수는~/net/ethernet/eth.c에 정의되어 있으며패킷의 타입을 결정해 주며프로토콜의 ID를 넘겨준다따라서이 함수의 복귀값으로 소켓 버퍼의 protocol필드를 채우고, dev 필드에는 현재의 네트워크 디바이스 구조체를 표시한다. LOOPBACK_MUST_CHECKSUM이 설정된 경우에는 소켓 버퍼의 ip_summed CHECKSUM_UNNECESSARY로 주어서 IP layer에서 checksum을 하지 않도록 만든다.

netif_rx()[14]함수는 상위의 laery에 패킷이 도착했음을 알리는 함수로서상위에 있는 layer가 패킷을 처리할 수 있도록 받은 패킷을 큐잉(queueing)한다나머지는 통계정보를 업데이트 하는 부분이다. rx_bytes tx_bytes, rx_packets tx_packets등의 정보가 갱신된다.

get_stats()함수는 디바이스 드라이버가 관리하는 통계정보를 알려주는 역할을 한다단순히net_device구조체의 priv에 할당한 net_device_stats구조체를 돌려주기만 하면 된다.

 

물론 이곳에서 보여주는 것은 하드웨어에 독립적인 부분들에 대한 코드만을 간단히 다루었을 뿐이다.더 자세한 구현을 보기를 원하는 사람들을 위해서 Appendix에 커널 버전 2.0에서 작가가 구현한 디바이스 드라이버의 코드와 설명을 보기 바란다.

1.1.8.   특정 운영 체제에서의 구현

대부분의 운영 체제는 드라이버와의 인터페이스를 정의하고 있으며네트워크 디바이스역시 예외가 아니다중요한 핵심부분에 대한 이해만 있으며, DDK(device driver development kit) (tool) 이용해서 인터페이스에 맞게 구현하면 된다는 말이다이것은 하나의 드라이버를 개발한 상태에서 새로운 운영 체제로 포팅(porting)하게 되는 경우에 해당한다.

가령 예를 들어, Windows NT 경우에는 NDIS(Network Driver Interface Specification) 따라 작성되며하드웨어에 의존적인 부분만을 새로이 코딩(coding) 주면 된다또한 SCO UNIX 경우에도 마찬가지로 드라이버 문서들에서 명시된 방법들과 드라이버와 커널 인터페이스만을 만족하도록 해주면 포팅은 쉬운 과정이 된다물론 그에 따라익히고 보아야  문서들의 분량은 많다이것은 단순한 시간의문제가  뿐이다참고로 SCO Unix에서는 네트워크 디바이스 드라이버에 대해서 Stream Driver라는말을 사용하고 있으며www.sco.com에서 디바이스 드라이버에 대한 개발 툴과 예제 드라이버를 찾을 있을 것이다.

 


[1] CAM(Contents Addressable Memory)라는 것을 가지고 multicast address 대한 요구를 처리한다.

[2] SCO unix에서의 network driver stream이라는 것으로 이와 같은 일을 해주고 있다.

[3] CRC packet data 정확한 지를 점검하기 위한 목적으로 전달되며주로 polynomial 나눈 나머지값을 setting한다.

[4] Qdisc구조체는 패킷의 보내기와 받기를 위한 패킷의 큐를 말하는 것으로 해당 디바이스 드라이버와 연결되어있다패킷을 보내거나 받기를 원하면 이곳에 패킷을 두고스캐줄링을 요청하면 해당 디바이스 드라이버나 상위의 프로토콜이 처리해  것이다.

[5] DMA 같은 연산에서 하드웨어가 연속되지 않은 메모리 영역에 대해서도 DMA 가능한 경우를 나타낸다.

[6] 디바이스 내의 FIFO(buffer)에서 에러를 일으킨 경우이다.

[7] 예를 들어서 네트워크 카드는 존재하지만네트워크 케이블이 빠진 경우가 있을  있다.

[8] Heart beat 영어 단어상 심장 박동을 의미하며네트워크 카드가 제대로 주기적인 동작을 나타내고 있지 못하다는 것을 말해준다.

[9] 실제적으로 device driver program이므로 device driver에서 사용하는 정적인 변수에 대해서는 이곳에선언하여 사용한다프로그램의 source에서  부연적으로 설명하도록 한다.

[10] 참고로 SCO Unix용의 device driver를 작성하게 될 경우에는 IEEE 802.3 standard에서 제공하여야 할 필요 사항으로 Device driver kit document에 명시되어 있다따라서적어도 이 정도의 정보는 응용 프로그램에 알려주어야 할 것이다.

[11] 아래와 같이 정의되어 있다.

#ifdef CONFIG_MODULES

#define SET_MODULE_OWNER(some_struct) do { (some_struct)->owner = THIS_MODULE; } while (0)

#else

#define SET_MODULE_OWNER(some_struct) do { } while (0)

#endif

[12] ~/net/core/dev.c 참조하기 바란다.

[13] ~/include/linux/if_ether.h 정의되어 있다.

[14] ~/net/core/dev.c 정의되어 있다.



 
TRACKBACKS 0    COMMENTS 0

 


비밀글로 저장  
 
 
<<이전   | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ··· | 730 |   다음>>
 

rednine's Blog is powered by Daum & tistory