L2 Switch를 만들고 싶습니다.
글쓴이: cjy1126 / 작성시간: 목, 2004/01/15 - 10:04오전
http://www.f-net.co.kr/index.html?node=02020105&sub=2#2
이 제품과 비슷한 기능을 가진 L2 Switch를 만들려고 합니다.
이 L2 Switch를 구현하려는 컴퓨터는 Linux 2.4.24에 랜카드가 5개 있습니다.
제가 생각하는 구성도는 2가지입니다.
1번째 구성도입니다.
Router | Firewall | +--- NIDS L2 Switch -- << 이 L2 Switch를 구현하려 합니다. | +--- NIDS | | Host << 서비스를하는 서버가 될수도 있고, 일반 클라이언트가 될수도 있습니다. 패킷의 흐름입니다. Route -> Firewall -> L2 Switch ---> Host | ---------> NIDS L2 Switch에서 Host에 패킷을 흘리면서 동시에 한대의 NIDS에 패킷을 보내줍니다. NIDS의 선택은 NIDS의 리소스상태, 트래픽을보고 부하가 적은것으로 선택합니다.
2번째 구성도 입니다.
Router | Firewall | +--- IPS L2 Switch -- << 이 L2 Switch를 구현하려 합니다. | +--- IPS | | Host << 서비스를하는 서버가 될수도 있고, 일반 클라이언트가 될수도 있습니다. 패킷의 흐름입니다. Route -> Firewall -> L2 Switch -> IPS -> Host 1. L2 Switch에서 Firewall쪽에 있는 랜카드에서 받은 패킷이면 한대의 IPS로 패킷을 전송합니다. 2. IPS를 통과한 패킷은 다시 L2 Switch로 들어옵니다. 3. L2 Switch는 IPS와 연결된 랜카드에서 온 패킷(안전한 패킷)임을 알고로 Host로 패킷을 전송합니다. IPS의 선택은 NIDS와 동일한 방법을 사용합니다.
가능하면 옵션을줘서 2가지가 다 되게하려고 합니다.
http://open-source.arkoon.net/kernel/kernel_net.png
일단 ip계층을 통과안시키는 허브의 역할을 하기위해서 netif_rx함수에서 패킷을 잡고, 이 패킷을 dev_queue_xmit함수로 보내려합니다.
현재 TEST하는 구성도입니다.
| 203.227.xxx.246 Route | 192.168.100.1 | | 192.168.100.11 Firewall | 192.168.100.12 | | 192.168.100.25 (eth0) L2 Switch | 203.227.xxx.248 | | 203.227.xxx.243 Host A 1. 203.227.xxx.243의 default gw은 203.227.xxx.248로 잡았습니다. 2. Host A에서 Firewall의 IP인 192.168.100.12로 ping을 쏩니다. 3. L2 Switch는 받은 패킷의 source MAC이 Host A의 MAC이면 그 패킷의 정보를 수정한후 dev_queue_xmit으로 보냅니다. 수정하는 정보: 1. skb->mac.ethernet->h_dest (192.168.100.12의 MAC로) 2. skb->dev = dev_get_by_name("eth0"); (패킷을 쏠 device를 192.168.100.25의 device pointer로)
원래 dev.c의 netif_rx
int netif_rx(struct sk_buff *skb) { int this_cpu = smp_processor_id(); struct softnet_data *queue; unsigned long flags; if (skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp); /* The code is rearranged so that the path is the most short when CPU is congested, but is still operating. */ queue = &softnet_data[this_cpu]; local_irq_save(flags); netdev_rx_stat[this_cpu].total++; if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { if (queue->throttle) goto drop; enqueue: dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue,skb); local_irq_restore(flags); #ifndef OFFLINE_SAMPLE get_sample_stats(this_cpu); #endif return queue->cng_level; } if (queue->throttle) { queue->throttle = 0; #ifdef CONFIG_NET_HW_FLOWCONTROL if (atomic_dec_and_test(&netdev_dropping)) netdev_wakeup(); #endif } netif_rx_schedule(&queue->blog_dev); goto enqueue; } if (queue->throttle == 0) { queue->throttle = 1; netdev_rx_stat[this_cpu].throttled++; #ifdef CONFIG_NET_HW_FLOWCONTROL atomic_inc(&netdev_dropping); #endif } drop: netdev_rx_stat[this_cpu].dropped++; local_irq_restore(flags); kfree_skb(skb); return NET_RX_DROP; }
바꾼 netif_rx
int netif_rx(struct sk_buff *skb) { int this_cpu = smp_processor_id(); struct softnet_data *queue; unsigned long flags; struct ethhdr temp; temp.h_source[0] = 0x00; /*제가 test로 접속하는 컴퓨터의 MAC주소입니다.*/ temp.h_source[1] = 0x03; temp.h_source[2] = 0x47; temp.h_source[3] = 0xe0; temp.h_source[4] = 0xb2; temp.h_source[5] = 0x2c; if (skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp); /* The code is rearranged so that the path is the most short when CPU is congested, but is still operating. */ queue = &softnet_data[this_cpu]; local_irq_save(flags); netdev_rx_stat[this_cpu].total++; if(!memcmp(temp.h_source, skb->mac.ethernet->h_source, 6)) /*받은 패킷이 제가 보낸 패킷이면*/ { printk("######### TEST #########\n"); skb->mac.ethernet->h_dest[0] = 0x00; /*패킷의 목적지 MAC에 192.168.100.12의 MAC 을 넣습니다.*/ skb->mac.ethernet->h_dest[1] = 0x01; skb->mac.ethernet->h_dest[2] = 0x03; skb->mac.ethernet->h_dest[3] = 0x45; skb->mac.ethernet->h_dest[4] = 0x61; skb->mac.ethernet->h_dest[5] = 0xd4; skb->dev = dev_get_by_name("eth0"); /*192.168.100.12와 붙어있는 device... 즉 192.168.100.25인 eth0의 포인터를 대입합니다. */ return dev_queue_xmit(skb); } /* 이 밑의 소스는 안건드렸습니다. */ if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { if (queue->throttle) goto drop; enqueue: dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue,skb); local_irq_restore(flags); #ifndef OFFLINE_SAMPLE get_sample_stats(this_cpu); #endif return queue->cng_level; } if (queue->throttle) { queue->throttle = 0; #ifdef CONFIG_NET_HW_FLOWCONTROL if (atomic_dec_and_test(&netdev_dropping)) netdev_wakeup(); #endif } netif_rx_schedule(&queue->blog_dev); goto enqueue; } if (queue->throttle == 0) { queue->throttle = 1; netdev_rx_stat[this_cpu].throttled++; #ifdef CONFIG_NET_HW_FLOWCONTROL atomic_inc(&netdev_dropping); #endif } drop: netdev_rx_stat[this_cpu].dropped++; local_irq_restore(flags); kfree_skb(skb); return NET_RX_DROP; }
원래 dev.c의 dev_queue_xmit
int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct Qdisc *q; if (skb_shinfo(skb)->frag_list && !(dev->features&NETIF_F_FRAGLIST) && skb_linearize(skb, GFP_ATOMIC) != 0) { kfree_skb(skb); return -ENOMEM; } /* Fragmented skb is linearized if device does not support SG, * or if at least one of fragments is in highmem and device * does not support DMA from it. */ if (skb_shinfo(skb)->nr_frags && (!(dev->features&NETIF_F_SG) || illegal_highdma(dev, skb)) && skb_linearize(skb, GFP_ATOMIC) != 0) { kfree_skb(skb); return -ENOMEM; } /* If packet is not checksummed and device does not support * checksumming for this protocol, complete checksumming here. */ if (skb->ip_summed == CHECKSUM_HW && (!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM)) && (!(dev->features&NETIF_F_IP_CSUM) || skb->protocol != htons(ETH_P_IP)))) { if ((skb = skb_checksum_help(skb)) == NULL) return -ENOMEM; } /* Grab device queue */ spin_lock_bh(&dev->queue_lock); q = dev->qdisc; if (q->enqueue) { int ret = q->enqueue(skb, q); qdisc_run(dev); spin_unlock_bh(&dev->queue_lock); return ret == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : ret; } /* The device has no queue. Common case for software devices: loopback, all the sorts of tunnels... Really, it is unlikely that xmit_lock protection is necessary here. (f.e. loopback and IP tunnels are clean ignoring statistics counters.) However, it is possible, that they rely on protection made by us here. Check this and shot the lock. It is not prone from deadlocks. Either shot noqueue qdisc, it is even simpler 8) */ if (dev->flags&IFF_UP) { int cpu = smp_processor_id(); if (dev->xmit_lock_owner != cpu) { spin_unlock(&dev->queue_lock); spin_lock(&dev->xmit_lock); dev->xmit_lock_owner = cpu; if (!netif_queue_stopped(dev)) { if (netdev_nit) dev_queue_xmit_nit(skb,dev); if (dev->hard_start_xmit(skb, dev) == 0) { dev->xmit_lock_owner = -1; spin_unlock_bh(&dev->xmit_lock); return 0; } } dev->xmit_lock_owner = -1; spin_unlock_bh(&dev->xmit_lock); if (net_ratelimit()) printk(KERN_CRIT "Virtual device %s asks to queue packet!\n", dev->name); kfree_skb(skb); return -ENETDOWN; } else { /* Recursion is detected! It is possible, unfortunately */ if (net_ratelimit()) printk(KERN_CRIT "Dead loop on virtual device %s, fix it urgently!\n", dev->name); } } spin_unlock_bh(&dev->queue_lock); kfree_skb(skb); return -ENETDOWN; }
제가 바꾼 dev_queue_xmit
int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; /* Grab device queue */ spin_lock_bh(&dev->queue_lock); dev->hard_start_xmit(skb, dev); spin_unlock_bh(&dev->xmit_lock); return 0; }
여기까지가 제가한 방법입니다.
제가 이상하게 생각하거나 혹은, 이상하게 구현하는 부분에대해서 조언부탁드립니다.(TEST 구성도의 IP는 오늘 /16이나 /32로 subnet 할것입니다.)
혹시 MAC을 이용한 LoadBalance, L2 Layer 관련자료가 있으시면 도움 부탁드리겠습니다.
참조한 책은 "리눅스 매니아를 위한 커널 프로그래밍", "Linux Programming Bible" 이고, 사이트는 열거도 못하겠네요.(너무 많이 들어가서, 저도 어디를 갔었는지 -_-;;; 찾다보면 전에 본곳이고...)
커널 프로그래밍이 처음이라서 똑같아도 난감한데, 2.4.20부터 소스가 바뀌었다던데... 권수호님의 책과 다른부분이 꽤 있네요 ㅜ.ㅜ
Forums:
dev_queue_xmit 에서 printk를 하면 안되나요?
계속 커널 패닉이 나네요.
커널 패닉이나면서 나오는 메세지를 읽을줄 몰라서... 조금씩 고쳐서 다시 컴파일하면서 찾아가네요.
5시간동안 커널 컴파일만 30번넘었네요 ㅜ.ㅜ
혹시 netif_rx와 dev_queue_xmit 분석된곳이 없나요?
FAST_ROUTE처럼 그냥 return dev_queue_xmit(skb); 이렇게 한거거든요.
dev_queue_xmit에서 dev->hard_start_xmit을 이용해서 device에 skb를 전달해서 패킷을 전송하는게 아닌가요?
댓글 달기