수신 받은 패킷을 체크하여 다른 인터페이스로 전송하는 방법좀 알려주세요.
관심 가져 주셔서 감사합니다.
며칠째 계속 이거 하나 때문에 고생하고 있습니다. 좀 도와 주세요.
디바이스 드라이버에서 패킷을 수신 받으면 netif_rx() -> net_rx_action() -> netif_receive_skb()
함수를 거쳐서 커널내의 라우팅 루틴으로 흘러 들어가는걸로 알고 있습니다.
제가 하려고 하는 작업은 netif_rx(), net_rx_action(), netif_receive_skb() 함수내에서 패킷을
검사하여 지정한 MAC 주소에서 송신되는 패킷은 커널내의 라우팅 루틴을 거치지 않고 바로 전송하려고
하는 겁니다. 패킷이 라우팅 루틴으로 흘러 들어가면 CPU 점유율이 100%까지 올라가고 전송속도도
제대로 나오지 않아서 생각한 방법입니다. 여기 kldp 게시판에도 저와 비슷한 작업을 하시려는 분들도
있는걸로 보아 분명히 가능하리라 생각 됩니다. 아래는 제가 수정한 코드인데 커널 패닉도 발생하고
체크섬이 틀려서 받는쪽에서 패킷을 그냥 버리더라구요. 커널 패닉은 skb 버퍼의 ip 주소와 mac 주소
변경시 발생합니다. 체크섬 생성 코드는 게시판 검색해서 넣어 보았는데 어떤 패킷은은 체크섬이 정상적으로 들어가고
어떤 패킷은 엉터리로 들어가네요.
정리해서 질문 드리겠습니다.
1. 수신 받은 패킷의 skb 버퍼내의 IP 주소와 MAC 주소 변경을 커널 패닉(메모리 공유 오류로 판단됨) 현상이 발생하지 않도록
변경하는 방법을 알고 싶습니다. skb 버퍼 관련해서 api도 찾아 보았지만 어떤걸 사용해야 할지 모르겠습니다.
2. 1번에서 ip 주소와 mac 주소 변경하였으므로 체크섬을 생성해야 하는데 IP 레이어, TCP 레이어의 체크섬을 각각 어떤 함수를
사용해서 변경을 해야 하는지요?
이상입니다. 관심가져 주셔서 감사드리며 즐거운 하루 되세요.
// eth0 MAC 주소 : 00:12:34:56:78:90 // eth0 IP 주소 : 192.168.1.1 // eth1 MAC 주소 : 00:12:34:56:78:91 // eth1 IP 주소 : 192.168.10.101 // PC1(client) MAC 주소 : 00:01:02:03:04:05 // PC1 IP 주소 : 192.168.1.2 // PC2(server) MAC 주소 : 00:01:02:03:04:06 // PC2 IP 주소 : 192.168.10.18 int netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; int ret = NET_RX_DROP; unsigned short type = skb->protocol; struct iphdr *iph = skb->h.ipiph; struct iphdr *niph; struct tcphdr *tcph; int i; struct iphdr *iph_ck = skb->nh.iph; struct tcphdr *tcph_ck = (void *)iph_ck + iph_ck->ihl*4; u_int32_t tcplen = skb->len - iph_ck->ihl*4; u_int32_t datalen = tcplen - tcph_ck->doff*4; __u8 *ipsaddr; __u8 *ipdaddr; ipsaddr = (__u8*)&(iph->saddr); ipdaddr = (__u8*)&(iph->daddr); if (skb->stamp.tv_sec == 0) do_gettimeofday(&skb->stamp); skb_bond(skb); netdev_rx_stat[smp_processor_id()].total++; TRACE_NETWORK(TRACE_EV_NETWORK_PACKET_IN, skb->protocol); // eth0 에서 들어오는 패킷중 PC1에서 보내는 패킷이라면 PC2로 그냥 전송함 if( (strcmp(skb->dev->name, "eth0") == 0) ) { if( (skb->mac.ethernet->h_source[0] == 0x00) && (skb->mac.ethernet->h_source[1] == 0x01) && (skb->mac.ethernet->h_source[2] == 0x02) && (skb->mac.ethernet->h_source[3] == 0x03) && (skb->mac.ethernet->h_source[4] == 0x04) && (skb->mac.ethernet->h_source[5] == 0x05) && (skb->mac.ethernet->h_dest[0] == 0x00) && (skb->mac.ethernet->h_dest[1] == 0x01) && (skb->mac.ethernet->h_dest[2] == 0x02) && (skb->mac.ethernet->h_dest[3] == 0x03) && (skb->mac.ethernet->h_dest[4] == 0x04) && (skb->mac.ethernet->h_dest[5] == 0x06) ) { printk("\neth0 receive(%d Bytes)!\n", skb->len); *(ipsaddr+2) = 10; // 소스 아이피 주소 변경 192.168.10.101 *(ipsaddr+3) = 101; skb->mac.ethernet->h_source[0] = 0x00; // eth1의 MAC 주소 skb->mac.ethernet->h_source[1] = 0x12; skb->mac.ethernet->h_source[2] = 0x34; skb->mac.ethernet->h_source[3] = 0x56; skb->mac.ethernet->h_source[4] = 0x78; skb->mac.ethernet->h_source[5] = 0x91; skb->mac.ethernet->h_dest[0] = 0x00; // PC2의 MAC 주소 skb->mac.ethernet->h_dest[1] = 0x01; skb->mac.ethernet->h_dest[2] = 0x02; skb->mac.ethernet->h_dest[3] = 0x03; skb->mac.ethernet->h_dest[4] = 0x04; skb->mac.ethernet->h_dest[5] = 0x06; skb->dev = dev_get_by_name("eth1"); // IP 헤더 체크섬 ip_send_check(iph); // TCP 체크섬 niph = skb->nh.iph; tcph = (void *)niph + niph->ihl*4; datalen = skb->len - niph->ihl*4; tcph->check = 0; tcph->check = tcp_v4_check(tcph, datalen, niph->saddr, niph->daddr, csum_partial((char *)tcph, datalen, 0)); skb_push(skb, skb->dev->hard_header_len); printk("source mac : %02X:%02X:%02X:%02X:%02X:%02X\n", skb->mac.ethernet->h_source[0], skb->mac.ethernet->h_source[1], skb->mac.ethernet->h_source[2], skb->mac.ethernet->h_source[3], skb->mac.ethernet->h_source[4], skb->mac.ethernet->h_source[5]); printk("dest mac : %02X:%02X:%02X:%02X:%02X:%02X\n", skb->mac.ethernet->h_dest[0], skb->mac.ethernet->h_dest[1], skb->mac.ethernet->h_dest[2], skb->mac.ethernet->h_dest[3], skb->mac.ethernet->h_dest[4], skb->mac.ethernet->h_dest[5]); for( i = 0; i < skb->len; i++ ) { printk("%02X ", skb->data[i]); if( ((i+1) % 8) == 0 ) { printk(" "); } if( ((i+1) % 16 == 0) ) { printk("\n"); } } printk("\n"); return dev_queue_xmit(skb); } } // eth1 에서 들어오는 패킷중 PC2에서 보내는 패킷이라면 PC1로 그냥 전송함 else if( (strcmp(skb->dev->name, "eth1") == 0) ) { if( (skb->mac.ethernet->h_source[0] == 0x00) && (skb->mac.ethernet->h_source[1] == 0x01) && (skb->mac.ethernet->h_source[2] == 0x02) && (skb->mac.ethernet->h_source[3] == 0x03) && (skb->mac.ethernet->h_source[4] == 0x04) && (skb->mac.ethernet->h_source[5] == 0x06) && (skb->mac.ethernet->h_dest[0] == 0x00) && (skb->mac.ethernet->h_dest[1] == 0x12) && (skb->mac.ethernet->h_dest[2] == 0x34) && (skb->mac.ethernet->h_dest[3] == 0x56) && (skb->mac.ethernet->h_dest[4] == 0x78) && (skb->mac.ethernet->h_dest[5] == 0x91) ) { printk("\neth1 receive(%d Bytes)!\n", skb->len); *(ipsaddr+2) = 10; *(ipsaddr+3) = 18; *(ipdaddr+2) = 1; *(ipdaddr+3) = 2; skb->mac.ethernet->h_source[0] = 0x00; // eth0 의 MAC 주소 skb->mac.ethernet->h_source[1] = 0x12; skb->mac.ethernet->h_source[2] = 0x34; skb->mac.ethernet->h_source[3] = 0x56; skb->mac.ethernet->h_source[4] = 0x78; skb->mac.ethernet->h_source[5] = 0x90; skb->mac.ethernet->h_dest[0] = 0x00; // PC1의 MAC 주소 skb->mac.ethernet->h_dest[1] = 0x01; skb->mac.ethernet->h_dest[2] = 0x02; skb->mac.ethernet->h_dest[3] = 0x03; skb->mac.ethernet->h_dest[4] = 0x04; skb->mac.ethernet->h_dest[5] = 0x05; skb->dev = dev_get_by_name("eth0"); // IP 헤더 체크섬 ip_send_check(iph); // TCP 체크섬 iph = skb->nh.iph; tcph = (void *)iph + iph->ihl*4; datalen = skb->len - iph->ihl*4; tcph->check = 0; tcph->check = tcp_v4_check(tcph, datalen, iph->saddr, iph->daddr, csum_partial((char *)tcph, datalen, 0)); skb_push(skb, skb->dev->hard_header_len); printk("source mac : %02X:%02X:%02X:%02X:%02X:%02X\n", skb->mac.ethernet->h_source[0], skb->mac.ethernet->h_source[1], skb->mac.ethernet->h_source[2], skb->mac.ethernet->h_source[3], skb->mac.ethernet->h_source[4], skb->mac.ethernet->h_source[5]); printk("dest mac : %02X:%02X:%02X:%02X:%02X:%02X\n", skb->mac.ethernet->h_dest[0], skb->mac.ethernet->h_dest[1], skb->mac.ethernet->h_dest[2], skb->mac.ethernet->h_dest[3], skb->mac.ethernet->h_dest[4], skb->mac.ethernet->h_dest[5]); for( i = 0; i < skb->len; i++ ) { printk("%02X ", skb->data[i]); if( ((i+1) % 8) == 0 ) { printk(" "); } if( ((i+1) % 16 == 0) ) { printk("\n"); } } printk("\n"); return dev_queue_xmit(skb); } } #ifdef CONFIG_NET_FASTROUTE if (skb->pkt_type == PACKET_FASTROUTE) { netdev_rx_stat[smp_processor_id()].fastroute_deferred_out++; return dev_queue_xmit(skb); } #endif skb->h.raw = skb->nh.raw = skb->data; pt_prev = NULL; for (ptype = ptype_all; ptype; ptype = ptype->next) { if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) { if (!pt_prev->data) { ret = deliver_to_old_ones(pt_prev, skb, 0); } else { atomic_inc(&skb->users); ret = pt_prev->func(skb, skb->dev, pt_prev); } } pt_prev = ptype; } } #ifdef CONFIG_NET_DIVERT if (skb->dev->divert && skb->dev->divert->divert) ret = handle_diverter(skb); #endif /* CONFIG_NET_DIVERT */ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) if (skb->dev->br_port != NULL && br_handle_frame_hook != NULL) { return handle_bridge(skb, pt_prev); } #endif for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) { if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) { if (!pt_prev->data) { ret = deliver_to_old_ones(pt_prev, skb, 0); } else { atomic_inc(&skb->users); ret = pt_prev->func(skb, skb->dev, pt_prev); } } pt_prev = ptype; } } if (pt_prev) { if (!pt_prev->data) { ret = deliver_to_old_ones(pt_prev, skb, 1); } else { ret = pt_prev->func(skb, skb->dev, pt_prev); } } else { kfree_skb(skb); /* Jamal, now you will not able to escape explaining * me how you were going to use this. :-) */ ret = NET_RX_DROP; } return ret; }
제가 실수했네요..
bptable 이 아니고
ebtable 이었어요
원하시는게, 하나의 PC에 1개의 외부 망과 연결된 NIC 가 있고, n 개의 내부망 NIC가 있는 경우,
외부망으로 부터 들어오는 Packet 들 중 일부를 내부망의 특정 PC로 바로 포워딩 시켜버리는 것이 맞다면
ebtables 이 맞을거에요.
중간 브리지 서버는 단순히 내부망으로 들어가는 모든 패킷들을 감시할 수 있고, 필요하다면 여러가지 패킷 작업을 할 수 도 있겠죠.
http://ebtables.sourceforge.net/
사이트는 위이고요
원하시는것이 맞기를 바랍니다.
그럼 고운 하루 보내세요.
http://nicesj.com
https://nicesj.com
https://blog.nicesj.com
감사합니다.
아~ ebtables 였군요...
자세히는 못봤지만 아마도 가능할것 같네요.
계속 신경써 주셔서 감사합니다.
ebtables 분석좀 하고 결과 올리겠습니다.
수고하세요.
댓글 달기