『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)〠736번 ì œ 목:[강좌]Network Device Driver만들기 [1] 올린ì´:hetta (ì´ê¸°ì²œ ) 01/10/21 11:38 ì½ìŒ:334 ê´€ë ¨ìžë£Œ ì—†ìŒ ----------------------------------------------------------------------------- ######################################### 강좌: Network Device Driver만들기 ## (부재: ì• ì¸ë§Œë“¤ê¸°) ######################################### 1ë¶€: ë„¤íŠ¸ì›Œí¬ ë“œë¼ì´ë²„ì˜ ê¸°ë³¸(1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ì´ì•¼ê¸°ê¾¼:ì´ê¸°ì²œ(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ì— ì‚¬ìš©ë˜ì§€ ì•ŠëŠ”ê²ƒì€ ë°°ì œí•˜ì˜€ë‹¤. í•„ìš”í•œê²ƒì€ í•„ìš”í•œë•Œ... ^_^ 그럼 다ìŒì‹œê°„ì—.... :) 『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)〠737번 ì œ 목:[강좌]Network Device Driver만들기 [2] 올린ì´:hetta (ì´ê¸°ì²œ ) 01/10/21 17:42 ì½ìŒ:109 ê´€ë ¨ìžë£Œ ì—†ìŒ ----------------------------------------------------------------------------- ######################################## #강좌: Network Device Driver만들기 # # (부재: ì• ì¸ë§Œë“¤ê¸°) # ######################################## 1ë¶€: ë„¤íŠ¸ì›Œí¬ ë“œë¼ì´ë²„ì˜ ê¸°ë³¸(2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ì´ì•¼ê¸°ê¾¼:ì´ê¸°ì²œ(hetta@nownuri.net) 네트ì›ìž¥ì¹˜ë¥¼ 열기와 닫기 ~~~~~~~~~~~~~~~~~~~~~~~~ ì´ë¶€ë¶„ì€ ì¡°ê¸ˆ 중복ë˜ëŠ”ë‚´ìš©ë„ ìžˆë‹¤. ì •ë¦¬í•˜ëŠ”ê¸°ë¶„ìœ¼ë¡œ ë³´ë©´ë 것같다. ifconfig eth0 111.111.111.111 up ì´ëŸ°ì‹ìœ¼ë¡œ í–ˆì„때 다ìŒê³¼ ê°™ì€ ë‘가지 ì‚¬ê±´ì´ ë°œìƒí•œë‹¤. 첫째로 ioctl(SIOCSIFADDR)로 ì¸í„°íŽ˜ì´ìŠ¤ì£¼ì†Œë¥¼ 세팅한다. ì´ê²ƒì€ ë“œë¼ ì´ë²„ì—서 êµ¬í˜„í•˜ëŠ”ê²ƒì´ ì•„ë‹ˆë¼ ì»¤ë„ì—서 하는작업ì´ë‹¤. 둘째로, ioctl(SIOCSIFFLAGS)를 사용하여 dev->flag를 IFF_UP으로 ë§Œë“ ë‹¤.랜카드 를 쓸수있ë„ë¡ í™œì„±í™”ì‹œí‚¤ëŠ”ê²ƒì´ë‹¤. ê·¸ë‹¤ìŒ net_deviceì—서 ì •ì˜í•œ open함수를 실행한다. openì€ ì €ë²ˆì‹œê°„ì—ë„ ì„¤ëª…í–ˆë“¯ì´, 장치가 필요로하는 ê°ì¢… ë¦¬ì†ŒìŠ¤ë“¤ì„ í• ë‹¹ë°›ëŠ” 다.그밖ì—ë„ ì—¬ëŸ¬ê°€ì§€ ìž‘ì—…ì´ Openì—서 실행ëœë‹¤. 첫째,하드웨어 주소를 dev->dev_addr ì— ë³µì‚¬í•œë‹¤. 둘째, openì—서는 ì¸í„°íŽ˜ì´ìŠ¤ì˜ ì „ì†¡í(interface's transmit queue)를 시작하기 위하여 netif_start_queue(struct net_device *dev); 를 호출하게 ëœë‹¤. ì´ìž‘ ì—…ì´ ì‹¤í–‰ë˜ì–´ì•¼ íŒ¨í‚·ì„ ì™¸ë¶€ë¡œ ì „ì†¡í• ìˆ˜ê°€ 있기때문ì´ë‹¤. ifconfig eth0 down ë“±ì˜ ëª…ë ¹ì„ í• ë•Œ close함수가 실행ë˜ê³ ì´ë•ŒëŠ”, netif_stop_queue()를 호출한다. ì´ê²ƒì€ netif_start_queue()와 반대가 ë˜ëŠ” 작업으로 íŒ¨í‚·ì„ ë”ì´ìƒ ì „ì†¡í• ìˆ˜ì—†ë„ë¡ í•œë‹¤. íŒ¨í‚·ì „ì†¡í•˜ê¸° ~~~~~~~~~~~~~ ê²°êµ ë„¤íŠ¸ì› ë””ë°”ì´ìФ 드ë¼ì´ë²„는 무슨ì¼ì„ í• ê¹Œ? "ìž˜ë°›ê³ ", "ìž˜ë³´ë‚´ê³ ", ì´ëŸ¬ëŠ”ê²ƒì´ ë“œë¼ì´ë²„ê°€ 하는 ì¼ì˜ ì „ë¶€ê°€ ì•„ë‹ê¹Œí•œë‹¤. ë‚˜ë¨¸ì§€ë“¤ì€ ì´ëŸ¬í•œ ìž‘ì—…ë“¤ì„ íš¨ìœ¨ì 으로 하ë„ë¡ ë„와주는 ì—í™œì„ í•˜ëŠ”ê²ƒì´ë‹¤. íŒ¨í‚·ì„ ë°›ëŠ”ê²ƒë³´ë‹¤ ë³´ë‚´ëŠ”ê²ƒì´ ë” ì‰½ê¸°ë•Œë¬¸ì— ë³´ë‚´ëŠ”ê²ƒì„ ë¨¼ì € 알아보ìž. íŒ¨í‚·ì„ ì „ì†¡í• í•„ìš”ê°€ 있ì„때마다, hard_start_transmit 함수가 호출ëœë‹¤.ì´í•¨ìˆ˜ ê°€ 하는ì¼ì€ 나가는í(outgoing queue)ì—다가 ë°ì´í„°ë¥¼ 놓는것ì´ë‹¤. 소켓버í¼(socket buffer)는 struct sk_buff 구조체로 만들어진다. 즉 í•˜ë‚˜ì˜ íŒ¨í‚·ì€ sk_buff구조체로 표현ëœë‹¤ê³ í• ìˆ˜ìžˆë‹¤. 다ìŒê³¼ ê°™ì´ ìƒê²¼ë‹¤ --------------------------------------------------------------------- struct sk_buff { /* These two members must be first. */ struct sk_buff * next; /* Next buffer in list */ struct sk_buff * prev; /* Previous buffer in list */ struct sk_buff_head * list; /* List we are on */ struct sock *sk; /* Socket we are owned by */ struct timeval stamp; /* Time we arrived */ struct net_device *dev; /* Device we arrived on/are leaving by */ /* Transport layer header */ union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct spxhdr *spxh; unsigned char *raw; } h; /* Network layer header */ union { struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; struct ipxhdr *ipxh; unsigned char *raw; } nh; /* Link layer header */ union { struct ethhdr *ethernet; unsigned char *raw; } mac; struct dst_entry *dst; /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ char cb[48]; unsigned int len; /* Length of actual data */ unsigned int data_len; unsigned int csum; /* Checksum */ unsigned char __unused, /* Dead field, may be reused */ cloned, /* head may be cloned (check refcnt to be sure). */ pkt_type, /* Packet class */ ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ atomic_t users; /* User count - see datagram.c,tcp.c */ unsigned short protocol; /* Packet protocol from driver. */ unsigned short security; /* Security level of packet */ unsigned int truesize; /* Buffer size */ unsigned char *head; /* Head of buffer */ unsigned char *data; /* Data head pointer */ unsigned char *tail; /* Tail pointer */ unsigned char *end; /* End pointer */ void (*destructor)(struct sk_buff *); /* Destruct function */ #ifdef CONFIG_NETFILTER /* Can be used for communication between hooks. */ unsigned long nfmark; /* Cache info */ __u32 nfcache; /* Associated connection, if any */ struct nf_ct_info *nfct; #ifdef CONFIG_NETFILTER_DEBUG unsigned int nf_debug; #endif #endif /*CONFIG_NETFILTER*/ #if defined(CONFIG_HIPPI) union{ __u32 ifield; } private; #endif #ifdef CONFIG_NET_SCHED __u32 tc_index; /* traffic control index */ #endif }; -------------------------------------------------------------------------- ë¬¼ë¡ ì´ëŸ° í•„ë“œë“¤ì„ ë‹¤ 알아야만 í•œë‹¤ëŠ”ê²ƒì€ ì•„ë‹ˆë‹¤. sk_buff구조체는 ìƒë‹¹ížˆ 복잡한 구조체ì´ë¯€ë¡œ ì´ê²ƒì„ 다루는 다양한 í•¨ìˆ˜ë“¤ì´ ì œê³µë˜ë©° ë’¤ì—서 ì„¤ëª…í• ê²ƒì´ë‹¤. struct sk_buff를 ì°¸ì¡°í• ë•Œ skb를 ì‚¬ìš©í•˜ê² ë‹¤. hard_start_xmitì´ í˜¸ì¶œë 때 ì „ë‹¬ë˜ëŠ” socket buffer는 완벽한 íŒ¨í‚·ì˜ í˜•íƒœë¥¼ 갖게ëœë‹¤.즉, ì¸í„°íŽ˜ì´ìФ(랜카드)ì—서 ì–´ë– í•œ ë°ì´í„° ìˆ˜ì •ë„ í•„ìš”ê°€ 없다. 그냥 ë¹„íŠ¸ì˜ ì—°ì†ì´ë¼ê³ ìƒê°í•˜ê³ ì „ì†¡í•˜ë©´ ëœë‹¤. skb->data는 ì „ì†¡ë˜ì–´ì§€ëŠ” íŒ¨í‚·ì˜ ë°ì´í„°ë¥¼ 가르킨다.skb->lenì€ ê¸¸ì´ë¥¼ 나타내며 단위는 옥텟(ë°”ì´íЏ)ì´ë‹¤. snullì—서는 snull_tx로 íŒ¨í‚·ì „ì†¡ì„ ì²˜ë¦¬í•œë‹¤. snullì— ëŒ€í•´ 세부ì 으로 ì•Œë ¤ê³ í• í•„ìš”ëŠ” ì—†ë‹¤ê³ ë³¸ë‹¤. 그러나 ì „ì²´ì ì¸ íë¦„ì€ ì‹¤ì œ 드ë¼ì´ë²„와 비슷하므로 여기서 ë³´ë„ë¡ í•˜ìž. ì €ë²ˆì‹œê°„ì— ë‚˜ì™”ë“¯ì´ ì´ˆê¸°í™”ì‹œ snull_initì—서 다ìŒê³¼ ê°™ì€ ì„¸íŒ…ì„ í•œë‹¤. int snull_init(struct net_device *dev) { ... dev->open = snull_open; dev->stop = snull_release; dev->set_config = snull_config; dev->hard_start_xmit = snull_tx; dev->do_ioctl = snull_ioctl; ... } 여기서 dev->hard_start_xmit ì´ snull_tx로 세팅ë˜ì—ˆìŒì„ 볼수있다. 그러므로 커ë„ì€ íŒ¨í‚·ì„ ì „ì†¡í•´ì•¼í• ê²½ìš°ì— snull_tx를 호출하게 ëœë‹¤. snull_txì˜ êµ¬í˜„ì„ ì‚´íŽ´ë³´ë„ë¡ í•˜ìž. ----------------------------------------------------------------------- /* * Transmit a packet (called by the kernel) */ int snull_tx(struct sk_buff *skb, struct net_device *dev) { int len; char *data; struct snull_priv *priv = (struct snull_priv *) dev->priv; len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; data = skb->data; dev->trans_start = jiffies; /* save the timestamp */ /* Remember the skb, so we can free it at interrupt time */ priv->skb = skb; /* actual deliver of data is device-specific, and not shown here */ snull_hw_tx(data, len, dev); //무시하ìž. return 0; /* Our simple device can not fail */ } --------------------------------------------------------------------- snullì—서는 ê°€ìƒìž¥ì¹˜ì´ë¯€ë¡œ í•˜ë“œì›¨ì–´ìž¥ì¹˜ì— ëŒ€í•œ ì‹¤ì œì ì „ì†¡ì„ snull_hw_tx()로 표현하였다. ì‹¤ì œì 하드웨어 ì „ì†¡ì€ DMAë“±ì„ ì‚¬ìš©í•˜ì—¬ ì „ì†¡í•œë‹¤.ì¼ë‹¨ë¬´ì‹œí•˜ìž. ì‹¤ì œì ìƒí™©ì—서 ë°œìƒí• 수있는 ë¬¸ì œ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ hard_start_xmit함수는 net_deviceì˜ xmit_lock로 race conditionë¬¸ì œë¥¼ 해결함 ì„ ì €ë²ˆì‹œê°„ì— ë³´ì•˜ë‹¤. 그러나 ì´ì „ìž‘ì—…ì´ ë나면 곧바로 다시 호출ëœë‹¤. 메모리가 부족하거나, 다른 ê³³ì—서 ê·¸ì‹œê°„ì— íŒ¨í‚·ì´ ì „ì†¡ì¤‘ì´ë¼ë©´ ìž ì‹œ ì „ì†¡ì„ ì§€ì—°í• í•„ìš”ê°€ 있다. ì´ë•Œ netif_stop_queue를 사용한다. ë˜í•œ ì „ì†¡ì´ ê°€ëŠ¥í•´ì§€ë©´ netif_wake_queue를 호출한다. void netif_wake_queue( struct net_devic *dev); netif_wake_queue는 netif_start_queue와 비슷하나, netif_wake_queue는 다시 ì „ì†¡ì„ ê°€ëŠ¥í•˜ê²Œ í•´ì¤€ë‹¤ëŠ”ê²ƒì´ ê°€ëŠ¥í•˜ë‹¤. 요즘나오는 하드웨어는 ì „ì†¡í를 여러개 ê°–ê³ ìžˆë‹¤. RTL8139ì—ì„œë„ 4ê°œì˜ ì „ì†¡í 를 ê´€ë¦¬í•¨ì„ í™•ì¸í• 수있다. ì „ì†¡ 타임아웃 ~~~~~~~~~~~~~~~ ì‹¤ì œ 네트ì›ìƒí™©ì€ 예측불허ì´ë¯€ë¡œ 여러가지 ìš”ì¸ìœ¼ë¡œ 패킷 ì „ì†¡ì„ í™•ì¸í•˜ëŠ” ackê°€ ì•ˆì˜¬ìˆ˜ë„ ìžˆë‹¤. ì´ëŸ´ê²½ìš°, 만약 íƒ€ìž„ì•„ì›ƒê°’ì„ ì„¸íŒ…í•´ë‘ì§€ 않는다면 ë¬´í•œì •ìœ¼ë¡œ íŒ¨í‚·ì „ì†¡ í™•ì¸ íŒ¨í‚·ì„ ê¸°ë‹¤ë¦¬ê²Œ ëœë‹¤. ì €ë²ˆì‹œê°„ì—ë„ ë‚˜ì˜¨ë‚´ìš©ì´ì§€ë§Œ 리눅스는 íƒ€ìž„ì•„ì›ƒê°’ì„ ì„¸íŒ…í•¨ìœ¼ë¡œ ì´ê²ƒì„ ë°©ì§€ 한다. íŒ¨í‚·ì´ dev->trans_startì— ì„¸íŒ…í•œ 값부터 dev->watchdog_timeo ê°’ì— ì„¸íŒ…í•œ jiffiesê°’ë§Œí¼ê¸°ë‹¤ë ¤ë„ íŒ¨í‚·ì „ì†¡ 확ì¸ì´ 안ë 경우 dev->tx_timeout함수가 호출ëœë‹¤. snullì˜ ê²½ìš°ëŠ” snull_tx_timeout함수가 호출ëœë‹¤. 다ìŒê³¼ ê°™ì´ êµ¬í˜„ë˜ì—ˆìœ¼ë©°, 대충 íë¦„ë§Œì„ ì•Œì•„ë³´ë„ë¡í•˜ìž. ------------------------------------------------------------------ /* * Deal with a transmit timeout. */ void snull_tx_timeout (struct net_device *dev) { struct snull_priv *priv = (struct snull_priv *) dev->priv; PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies, jiffies - dev->trans_start); priv->status = SNULL_TX_INTR; snull_interrupt(0, dev, NULL); priv->stats.tx_errors++; netif_wake_queue(dev); return; } ------------------------------------------------------------------ íƒ€ìž„ì•„ì›ƒì´ ë°œìƒí•˜ë©´ 놓친 ì¸í„°ëŸ½íŠ¸ë¥¼ 채우기위하여 snull_interrupt를 호출 한다. ê·¸ë¦¬ê³ ë‹¤ì‹œí•œë²ˆ ì „ì†¡ì„ ì‹œë„하게 ëœë‹¤. ( when a timeout happens in snull, the driver calls snull_nterrupt to fill in the "missing" interrupt and restarts the transmit queu with netif_wake_queue.) 패킷 받기 ~~~~~~~~~~ ì´ì œ íŒ¨í‚·ì„ ì–´ë–»ê²Œ ë°›ì„것ì¸ê°€ë¥¼ 알아보ë„ë¡í•˜ìž.íŒ¨í‚·ì„ ë°›ëŠ”ê²ƒì€ ì „ì†¡í•˜ëŠ”ê²ƒ 보다 조금 복잡하다. 사실 ì‹¤ì œ 하드웨어ì—서 í•˜ëŠ”ìž‘ì—…ì€ ë§Žì´ í‹€ë¦¬ë¯€ë¡œ snullì˜ ê²ƒì„ ë³´ëŠ”ê²ƒì´ ë³„ë„ì›€ì€ ì•ˆë˜ê² 지만, íë¦„ë§Œì„ ì•Œì•„ë³´ìž. ë¨¼ì € 랜카드ì—서 ë°›ì€ íŒ¨í‚·ì„ ì €ìž¥í• sk_buff를 í• ë‹¹í•´ì•¼í•œë‹¤.íŒ¨í‚·ì„ ë°›ëŠ”ê²ƒì€ interrupt를 통해서ì´ë‹¤. 즉, 랜카드ì—서 ì–´ëŠ ì¼ì •량ì´ìƒ ë°ì´í„°ê°€ 오면, 랜카드 ê°€ ì¸í„°ëŸ½ëŠë¥¼ ë°œìƒí•œë‹¤.그러면 드ë¼ì´ë²„ì—서 ìž‘ì—…ì„ ì²˜ë¦¬í•˜ëŠ”ì‹ì´ë‹¤. ì–´ëŠ ì¼ì • 량ì´ë¼ëŠ”ê²ƒì€ ìž„ìœ¼ë¡œ ì„¤ì •ì´ ê°€ëŠ¥í•˜ë‹¤. ë¬¼ë¡ polling ë°©ì‹ìœ¼ë¡œ 패킷받기를 ì²˜ë¦¬í•˜ëŠ”ê²ƒë„ ê°€ëŠ¥í•˜ë‚˜ 효율성면ì—서 interrupt를 ì´ìš©í•œ ë°©ì‹ì´ ìœ ë¦¬í•˜ë‹¤.대부분하드웨어가 interrupt를 사용한다. ----------------------------------------------------------------- /* * Receive a packet: retrieve, encapsulate and pass over to upper levels */ void snull_rx(struct net_device *dev, int len, unsigned char *buf) { struct sk_buff *skb; struct snull_priv *priv = (struct snull_priv *) dev->priv; /* * The packet has been retrieved from the transmission * medium. Build an skb around it, so upper layers can handle it */ skb = dev_alloc_skb(len+2); if (!skb) { printk("snull rx: low on mem - packet dropped\n"); priv->stats.rx_dropped++; return; } skb_reserve(skb, 2); /* align IP on 16B boundary */ memcpy(skb_put(skb, len), buf, len); /* Write metadata, and then pass to the receive level */ skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ priv->stats.rx_packets++; priv->stats.rx_bytes += len; netif_rx(skb); return; } ----------------------------------------------------------------- ë¨¼ì € dev_alloc_skb를 사용하여 skb를 위한 ê³µê°„ì„ ë§ˆë ¨í•œë‹¤. 여기서 +2는 16비트로 ì •ë ¬í•˜ê¸° 위하여 필요한 작업ì´ë‹¤.왜ëƒí•˜ë©´ ì´ë”ë„·í—¤ë”는 14비트 ì´ê¸°ë•Œë¬¸ì´ë‹¤. ê·¸ë‹¤ìŒ skb_reserve로 ì•žì— 2비트를 예비해둔다. ê·¸ë‹¤ìŒ memcpy를 사용하여 bufì˜ ë°ì´í„°ë¥¼ 복사하게 ëœë‹¤. skb_put()ì€ skbì˜ ë’¤ì— ë°ì´í„°ë¥¼ ë¶™ì´ê³ ìž í• ë•Œ 사용한다. 앞ì—서 2비트를 예비했으므로 그다ìŒì— ì´ë”ë„·í—¤ë” 14ë°”ì´íЏ ë“±ì´ ë“¤ì–´ê°€ì„œ 16비트로 ì •ë ¬(align)ëœë‹¤. ì°¸ê³ ë¡œ, dev_alloc_skb()는 ë‚´ë¶€ì 으로 kmalloc를 atomic priority로 호출하므로 ì¸í„°ëŸ½íЏ ë°œìƒì¤‘ì¼ë•Œ ì‚¬ìš©ì´ ê°€ëŠ¥í•˜ë‹¤. ê·¸ë‹¤ìŒ ì²´í¬ì„¬ì„ 어떻게 ì²˜ë¦¬í• ê²ƒì¸ê°€ë¥¼ 세팅한다. snullì€ ê°€ìƒìž¥ì¹˜ë¼ì„œ CHECKSUM_UNNECESSARYì„ ì„¸íŒ…í•˜ë‚˜ ì‹ ê²½ì“°ì§€ ë§ê¸° 바란다. 3가지가 ì¡´ìž¬í•˜ëŠ”ë° ë‹¤ìŒê³¼ 같다. CHECKSUM_HW - 하드웨어로 ì²´í¬ì„¬ì„ 관리한다. CHECKSUM_NONE - 소프트웨어로 ì²´í¬ì„¬ì„ 관리한다. ë””í´íЏ ê°’ì´ë‹¤. rtl8139 CHECK_UNNECESSARY - í•„ìš”ì—†ìŒ ê·¸ë¦¬ê³ protocolì€ eth_type_trans로 간단히 ì„¸íŒ…ì´ ê°€ëŠ¥í•˜ë‹¤. 최종ì 으로 netif_rx()를 호출하여서 윗단계(ex: IP layer)로 íŒ¨í‚·ì„ ì „ì†¡í•œë‹¤. ì¸í„°ëŸ½íЏ 핸들러 ~~~~~~~~~~~~~~~~ íŒ¨í‚·ì„ ì²˜ë¦¬í• ë•Œ ì¸í„°ëŸ½íŠ¸ë¥¼ ì´ìš©í•œë‹¤ê³ 했다. ì´ì œ ì¸í„°ëŸ½íЏ 핸들러를 살펴보ìž. 네트ì›ìž¥ì¹˜ì—서 ì¸í„°ëŸ½íŠ¸ëŠ” íŒ¨í‚·ì´ ì™”ì„때와 íŒ¨í‚·ì „ì†¡ì´ ì™„ë£Œë˜ì—ˆì„때 ë°œìƒë˜ë©°, ê°ê°ì˜ êµ¬ë¶„ì€ ëžœì¹´ë“œì˜ status register를 ì½ìŒìœ¼ë¡œì„œ 알수가 있다. snullì€ ê°€ìƒìž¥ì¹˜ì´ë¯€ë¡œ priviateë°ì´í„° ì˜ì—ì— ì´ìƒíƒœì •보를 ì €ìž¥í•˜ë‚˜,ì´ê²ƒì€ ì‹¤ì œ 필드ì—서 ì“°ëŠ”ë°©ë²•ì´ ì•„ë‹ˆë‹¤.! 그냥 무시하ìž. ------------------------------------------------------------------- /* * The typical interrupt entry point */ void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int statusword; struct snull_priv *priv; /* * As usual, check the "device" pointer for shared handlers. * Then assign "struct device *dev" */ struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */ if (!dev /*paranoid*/ ) return; /* Lock the device */ priv = (struct snull_priv *) dev->priv; spin_lock(&priv->lock); /* retrieve statusword: real netdevices use I/O instructions */ statusword = priv->status; if (statusword & SNULL_RX_INTR) { /* send it to snull_rx for handling */ snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata); } if (statusword & SNULL_TX_INTR) { /* a transmission is over: free the skb */ priv->stats.tx_packets++; priv->stats.tx_bytes += priv->tx_packetlen; dev_kfree_skb(priv->skb); } /* Unlock the device and we are done */ spin_unlock(&priv->lock); return; } ------------------------------------------------------------------- 만약 íŒ¨í‚·ì„ ë°›ì•˜ë‹¤ë©´ 위ì—서 설명한 snull_rx를 호출해서 ì²˜ë¦¬í•˜ê³ , íŒ¨í‚·ì „ì†¡ì™„ë£Œ ì¸í„°ëŸ½íЏë¼ë©´ í†µê³„ì •ë³´ë¥¼ ì„¸íŒ…í•œë‹¤ìŒ dev_kfree_skb로 skb를 위해 í• ë‹¹ëœ ë©”ëª¨ë¦¬ë¥¼ í•´ì œí•œë‹¤.즉, ackê°€ 올때까지는 ë©”ëª¨ë¦¬ì— íŒ¨í‚·ì„ ìœ„í•œ 메모리가 í• ë‹¹ëœì±„로 남아있는것ì´ë‹¤. ë§í¬ì˜ ìƒíƒœë¥¼ 변경하기 ~~~~~~~~~~~~~~~~~~~~~~~ (RTL8139ì—서 사용ë˜ì§„ 않는다. ì°¸ê³ ë§Œí•˜ìž.) ì¸ìœ„ì 으로 ëžœìƒì— carrierê°€ 존재하지 않는것처럼 í• ìˆ˜ìžˆë‹¤ëŠ”ê²ƒì´ë‹¤. 즉, ëžœì„ ì„ ë½‘ì•˜ë‹¤ë©´ carrierê°€ 사ë¼ì§€ê³ , 다시 꽂으면 carrierê°€ 있게ëœë‹¤. ë””í´íŠ¸ë¡œ ëžœì¹´ë“œì— carrierê°€ ì¡´ìž¬í•œë‹¤ê³ ê°€ì •ë˜ë‚˜,ë³€ê²½ë„ ê°€ëŠ¥í•˜ë‹¤. netif_carrier_on(strut net_device *dev); // carrier on 으로! netif_carrier_off(strut net_device *dev); //carrier off로! netif_carrier_ok(strut net_device *dev); //현재ìƒíƒœí™•ì¸. ë‚´ë¶€ì 으로 다ìŒê³¼ ê°™ì´ ì„¸íŒ…ë˜ì–´ìžˆì„ë¿ì´ë‹¤. 그냥 ì°¸ê³ ë§Œí•˜ìž. set_bitì€ atomic하게 비트를 세팅하는 함수ì´ë‹¤. include/linux/netdevice.h ----------------------------------------- 611 static inline void netif_carrier_off(struct net_device *dev) 612 { 613 set_bit(__LINK_STATE_NOCARRIER, &dev->state); 614 } ------------------------------------------------------------------- 소켓 버í¼(socket buffer) ~~~~~~~~~~~~~~~~~~~~~~~~~ 소켓버í¼ë¥¼ ë§Žì´ ì‚¬ìš©í•˜ì˜€ìœ¼ë‚˜ ìžì„¸í•œì„¤ëª…ì€ ì§€ê¸ˆê¹Œì§€ ìƒëžµë˜ì—ˆë‹¤. sk_buff는 ë¦¬ëˆ…ìŠ¤ì˜ ë„¤íŠ¸ì› ì½”ë“œì—서 ê°€ìž¥ì¤‘ì‹¬ì´ ë˜ëŠ” 구조체ì´ë‹¤. ì—¬ëŸ¬ê°œì˜ í•„ë“œë“¤ê³¼ sk_buff 를 다루는 다양한 ìœ í‹¸ë¦¬í‹° í•¨ìˆ˜ë“¤ì„ ì•Œì•„ë³´ë„ë¡ í•˜ìž. 중요 필드 ========================== 중요필드를 ë¨¼ì € 알아보ìž. struct net_device *rx_dev ; //소켓 버í¼ë¥¼ 받는장치 struct net_device *dev ; //소켓버í¼ë¥¼ 보내는 장치 // transport layer í—¤ë” ex) tcp, ucp... union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct spxhdr *spxh; unsigned char *raw; } h; // Network layer í—¤ë” ex) ip ... union { struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; struct ipxhdr *ipxh; unsigned char *raw; } nh; // Link layer í—¤ë” ex) ethernet union { struct ethhdr *ethernet; unsigned char *raw; } mac; ìœ„ì™€ê°™ì´ ë„¤íŠ¸ì›Œí¬ì˜ ê° ê³„ì¸µì— í•´ë‹¹í•˜ëŠ” í—¤ë”ë“¤ì´ ì •ì˜ë˜ì–´ìžˆë‹¤. <ì°¸ê³ >--------------------------------------------------------------- 예를들어서 source ip address , dest ip address 를 ì•Œê³ ì‹¶ë‹¤ê³ í•˜ìž. ì´ë•ŒëŠ” struct tcphdrì„ ì–»ì–´ì•¼í•˜ë¯€ë¡œ 그냥 skb->h.th 를 참조하면 ëœë‹¤. struct tcphdr ì€ include/linux/tcp.h ì—서 다ìŒê³¼ ê°™ì´ ì •ì˜í•œë‹¤. struct tcphdr { __u16 source; __u16 dest; __u32 seq; __u32 ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __u16 window; __u16 check; __u16 urg_ptr; }; 그러므로.... source ip address를 ì•Œë ¤ë©´ skb->h.th.source dest ip address를 ì•Œë ¤ë©´ skb->h.th.dest 로 하면 간단히 알수가 있다. ------------------------------------------------------------------------ ê·¸ë‹¤ìŒ ì¡°ê¸ˆ 복잡한 ë³€ìˆ˜ë“¤ì´ ì„ ì–¸ë˜ì–´ìžˆë‹¤. unsigned char *head ; unsigned char *data ; unsigned char *tail ; unsigned char *end ; í•„ìžë„ 좀 ì—‡ê°ˆë ¸ëŠ”ë°, ê¶Œìˆ˜í˜¸ë‹˜ì˜ ê¸€ì—있는 ê·¸ë¦¼ì„ ë³´ê³ ë‹¨ë²ˆì— ì´í•´ê°€ ë˜ì—ˆë‹¤. ì—¬ê¸°ì— ê·¸ë¦¬ê¸°ëŠ” 힘들므로 ê·¸ë¦¼ì€ ìƒëžµí•œë‹¤. ì‹œê°„ì´ í—ˆë½í•˜ì‹œëŠ”ë¶„ì€ ê¼! 살펴보기를 바란다. 간단히 설명하면 다ìŒê³¼ 같다. ìœ„ì˜ 4ê°œì˜ ë³€ìˆ˜ëŠ” ê²°êµ ë²„í¼ì˜ 주소를 ì €ìž¥í•˜ëŠ” í¬ì¸í„° 변수ì´ë‹¤. 버í¼ëŠ” head 로부터 end 까지가 ë˜ë©°, ì‹¤ì œë¡œ ë°ì´í„°ê°€ ìžˆëŠ”ê³³ì€ data로부터 tail까지가 ëœë‹¤.간단히 ê·¸ë ¤ë³´ë©´ 다ìŒê³¼ 같다. +------------------------------------------------------------------+ | | | | | | ì‹¤ì œ ì „ì†¡í• í˜¹ì€ ë°›ì€ ë°ì´í„° | | +------------------------------------------------------------------+ head data tail end 그러므로 사용가능한 버í¼í¬ê¸°ëŠ” skb->end - skb->head ê°€ ë˜ë©°, 현재사용ë˜ëŠ” ë°ì´í„°ì˜ í¬ê¸°ëŠ” skb->tail - skb->data ê°€ëœë‹¤. unsigned long len ; ì´ ê°’ì€ ë°ì´í„°ì˜ 길ì´ë¡œì„œ skb->tail - skb->data ê°€ëœë‹¤. unsigned char ip_summed ; ì²´í¬ì„¬ì„ 표시하는 ì •ì±…ìœ¼ë¡œì„œ 위ì—서 설명하였다. unsigned char pkt_type ; ì´ê²ƒì€ 패킷타입으로서 PACKET_HOST , PACKET_BROADCAST , PACKET_MULTICAST, PAKCET_OTHERHOSTì¤‘ì˜ í•˜ë‚˜ë¥¼ ì„¸íŒ…í• ì±…ìž„ì´ìžˆë‹¤.그러나 실질ì ì¸ ì„¸íŒ…ì€ ë‹¤ìŒê³¼ ê°™ì´ eth_type_trans()ì—서 세팅하므로 드ë¼ì´ë²„ 개발ìžëŠ” ì‹ ê²½ì„ ì“°ì§€ ì•Šì•„ë„ ëœë‹¤. skb->protocol = eth_type_trans (skb, dev); 소켓버í¼ë¥¼ 다루는 다양한 함수들 ================================= 소켓버í¼ë¥¼ 다루는 다양한 í•¨ìˆ˜ë“¤ì´ ì´ë¯¸ ì •ì˜ë˜ì–´ìžˆìœ¼ë¯€ë¡œ 소켓버í¼ë¥¼ 쉽게 ì‚¬ìš©í• ìˆ˜ê°€ 있다. struct sk_buff *alloc_skb(unsigned int len, int priority ); struct sk_buff *dev_alloc_skb(unsigned int len ) ; dev_alloc_skb는 GFP_ATOMIC권한으로 alloc_skb를 실행시킨다.ê·¸ë¦¬ê³ skb->data와 skb->head사ì´ì— ê³µê°„ì„ ì¡°ê¸ˆ 예비해준다. ì´ ê³µê°„ì€ network layerì—서 최ì í™”ì— ì‚¬ìš©ë˜ëŠ”ê²ƒìœ¼ë¡œì„œ 드ë¼ì´ë²„ì—서는 건들지 ë§ì•„야한다. 보통 드ë¼ì´ë²„ 개발ì—서는 dev_alloc_skb를 사용한다. void kfree_skb( struct sk_buff *skb); void dev_kfree_skb(struct sk_buff *skb); <<==ì´ê±¸ ì“°ìž! í• ë‹¹ë°›ì€ ê³µê°„ì„ í•´ì œí•œë‹¤.드ë¼ì´ë²„ 개발ìžëŠ” dev_kfree_skb를 사용해야한다. unsigned char * skb_put(struct sk_buff *skb, int len ); unsigned char * __skb_put(struct sk_buff *skb, int len ); unsigned char * skb_push(struct sk_buff *skb, int len ); unsigned char * __skb_push(struct sk_buff *skb, int len ); skb_putì€ ë²„í¼ì˜ tail ë’¤ì— ë¶™ì´ëŠ”ê²ƒìœ¼ë¡œ,변경ë˜ëŠ”ê°’ì€ tail,len ì´ ëœë‹¤. tailì€ ë‹¹ì—°ížˆ ëŠ˜ê² ê³ , lenë„ ëŠ˜ê²ƒì´ë‹¤. ì´ê²ƒì€ 버í¼ë¥¼ ëŠ˜ë¦¬ê¸°ì „ì˜ ì£¼ì†Œë¥¼ 리턴하므로 snullì—ì„œë„ ì´ê²ƒì„ 사용하여 memcpy로 복사했다. skb_push는 버í¼ì˜ 앞ì—다가 ë¶™ì´ëŠ”ê²ƒìœ¼ë¡œ 변경ë˜ëŠ”ê²ƒì€ data , lenì´ë‹¤. data는 당연히 ì¤„ê² ê³ , lenì€ ëŠ˜ê²ƒì´ë‹¤. ë¦¬í„´ê°’ì€ ë°©ê¸ˆ 만들어진 dataê°’ì´ ëœë‹¤. ë¦¬í„´ê°’ì„ ì‚´íŽ´ë³´ìžë©´, memcpy로 복사하기 쉬운 ê°’ì„ ê°ê° ë¦¬í„´í•¨ì„ ì•Œìˆ˜ìžˆë‹¤. __를 ë¶™ì¸ê²ƒì€ 그냥 간단히 ê¸°ëŠ¥ì€ ë˜‘ê°™ì€ë° ë°ì´í„°ë¥¼ 버í¼ì— ë³µì‚¬í• ë•Œ ê³µê°„ì´ ë§žëŠ”ì§€ë¥¼ ì²´í¬í•˜ëŠ” ì—´í• ì„ ë” í•œë‹¤. int skb_tailroom(struct sk_buff *skb); int skb_headroom(strut sk_buff *skb); 지금 버í¼ì— 얼마나 ê³µê°„ì´ ë‚¨ì•˜ë‚˜ë¥¼ 리턴하는것으로.... skb_tailì€ ë’·ìª½ê³µê°„ì„, skb_headroomì€ ì•žìª½ ë‚¨ì€ ê³µê°„ì„ ë¦¬í„´í•œë‹¤. 하지만 ì‹¤ì œì 으로 쓸ì¼ì€ ì—†ì„것ì´ë‹¤. 왜ëƒí•˜ë©´ skb_put ,skb_push를 í˜¸ì¶œí• ë•Œ ìžë™ìœ¼ë¡œ ì²´í¬í•´ì£¼ê¸° 때문ì´ë‹¤. void skb_reserve(struct sk_buff *skb , int len ); unsigned char *skb_pull(struct sk_buff *skb , int len ); skb_reserve는 ì•žìª½ì— ê³µê°„ì„ í™•ë³´í•˜ë¯€ë¡œ data,tailê°€ 변한다. snullì—ì„œë„ ì´ê²ƒ ì„ ì‚¬ìš©í•˜ì—¬ 16ë¹„íŠ¸ì •ë ¬ë¬¸ì œë¥¼ í•´ê²°í•¨ì„ ìœ„ì—서 보았다. skb_pullì€ íŒ¨í‚·ì˜ í—¤ë”로부터 ë°ì´í„°ë¥¼ ì œê±°í•œë‹¤. 드ë¼ì´ë²„는 ì´ìž‘ì—…ì´ í•„ìš”ê°€ 없다. 그냥 ì°¸ê³ ë§Œ 하기 바란다. 당연히 skb->len,skb->dataê°€ ê°ì†Œí• 것ì´ë‹¤. ì´ê²ƒì´ 바로 ethernet header,ip header, tcp header, udp header ë“±ì´ ë²—ê²¨ì§€ëŠ” ì›ë¦¬ì´ë‹¤.즉, 불필요한 메모리 복사가 í•„ìš”ì—†ì´ ê°„ë‹¨ížˆ skbì—서 몇몇 변수가 ì¡°ì •í•´ì£¼ë©´ ë˜ëŠ”ê²ƒì´ë‹¤. ì´ë²ˆê°•ì˜ë¥¼ 마치며 ~~~~~~~~~~~~~~~~~ ì´ë²ˆì‹œê°„까지 snullì— ëŒ€í•´ì„œ 알아보았다. ê²°êµ ë„¤íŠ¸ì› ë””ë°”ì´ìФ 드ë¼ì´ë²„란 ìž˜ë°›ê³ ,잘보내면 ëœë‹¤ëŠ”ê²ƒì„ ê¸°ì–µí–ˆìœ¼ë©´ 한다. 그러기 위하여, ì¸í„°ëŸ½íŠ¸ë¥¼ 사용하며, ì¼ì •량ì´ìƒì˜ ë°ì´í„°ê°€ 오면 랜카드가 ì¸í„°ëŸ½íŠ¸ë¥¼ 걸어서 "나좀 처리해줘.." ë¼ê³ ë§í•˜ë©°, 패킷 ì „ì†¡ì´ ì™„ë£Œë˜ì–´ë„ ì¸í„°ëŸ½íŠ¸ê°€ ë°œìƒí•˜ì—¬ì„œ í• ë‹¹ëœ ë²„í¼ë¥¼ í•´ì œí•˜ëŠ”ë“±ì˜ ì¼ì„ í•¨ì„ ì•Œì•„ë³´ì•˜ë‹¤. ë˜í•œ 소켓 버í¼ë¼ëŠ” struct skb_buffë„ ì•Œì•„ë³´ì•˜ë‹¤. ì‹¤ì œ ë„¤íŠ¸ì› ë””ë°”ì´ìФ 드ë¼ì´ë²„만드는 ê²ƒì€ ì´ëŸ¬í•œ ì´ë¡ 보다는.. ëžœì¹´ë“œë“±ì˜ í•˜ë“œì›¨ì–´ì˜ datasheetì„ ë”ë§Žì´ ë´ì•¼í• 것ì´ë‹¤. 하지만 여기서 설명한 ë‚´ìš©ë“¤ì´ ë¼ˆëŒ€ê°€ ë ê²ƒìž„ì€ ë¶„ëª…í•˜ë‹¤. ì´ê¸€ë¡œ ì¸í•´ ë…ìžë“¤ì´ ë˜ ë‹¤ë¥¸ 재미(funny)를 ë§Œë½í• 수만 있다면 ë” ë°”ëž„ê²ƒì´ ì—†ê² ë‹¤. 그럼 ë‹´ì‹œê°„ì— ^_^