netif_rx를 사용한 패킷 전송 관련 질문
글쓴이: bjlee72 / 작성시간: 금, 2008/02/15 - 3:01오후
안녕하세요? 질문으로 맨 처음 인사를 드리게 되어 송구하네요. ^^;
제가 요즘 Linux Kernel 2.6.18-1.2798.fc6에서 jprobe를 사용한
커널 모듈 프로그래밍을 하고 있습니다. 하다보니까 마음먹은대로 뭐가 잘 안되는군요.
제가 하는 일을 간단하게 설명드리면, jprobe로 커널의 특정 함수를 후킹한 다음,
후킹을 통해 얻은 정보를 구조체에 실어서 UDP 패킷 형태로 조립한 다음에
netif_rx 함수를 통해 lo 인터페이스로 보내어 127.0.0.1:59876에 대기하고 있는
UDP 서버로 전송을 하는 것입니다.
보내는 함수는 다음과 같이 생겨먹었습니다.
int report_send_(struct msghdr* msg, struct session_info* sinfo) { struct sk_buff* skb; int r; size_t ip_header_len = sizeof(struct iphdr); size_t udp_header_len = sizeof(struct udphdr); size_t session_info_len = sizeof(struct session_info); size_t len = ip_header_len + udp_header_len + session_info_len; struct iphdr ip_header = { .ihl = 5, .version = 4, .tos = 0, .tot_len = htons( (u16)len ), .id = 0, .frag_off = 0, .ttl = 2, .protocol = 17, .check = 0, .saddr = create_network_address_(127, 0, 0, 1), .daddr = create_network_address_(127, 0, 0, 1) // .saddr = htonl( 0x7f000001 ), // .daddr = htonl( 0x7f000001 ) }; struct udphdr udp_header = { .source = htons(59875), .dest = htons(59876), .len = htons( (u16)(udp_header_len + session_info_len) ), .check = 0 }; if ( !(skb = alloc_skb(len, GFP_ATOMIC)) ) { // cannot send report printk("report_send_() : alloc_skb() failed\n"); return -1; } skb->protocol = htons(ETH_P_IP); if ( !(skb->dev = dev_get_by_name("lo")) ) { printk("report_send_() : dev_get_by_name() failed\n"); return -1; } skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_LOOPBACK; memcpy(skb_put(skb, ip_header_len), (const char*)&ip_header, ip_header_len); memcpy(skb_put(skb, udp_header_len), (const char*)&udp_header, udp_header_len); memcpy(skb_put(skb, session_info_len), (const char*)sinfo, session_info_len); skb->nh.raw = skb->mac.raw = (unsigned char*)skb->data; skb->h.raw = (unsigned char*)skb->data + ip_header_len; // fix IP checksum ip_send_check(skb->nh.iph); // fix udp checksum skb->h.uh->check = csum_tcpudp_magic( skb->nh.iph->saddr, skb->nh.iph->daddr, udp_header_len + session_info_len, IPPROTO_UDP, csum_partial( (char*)skb->h.uh, udp_header_len+session_info_len, 0 ) ); //netif_rx_ni(skb); if ( (r = netif_rx(skb)) != NET_RX_SUCCESS ) { printk("report_send_() : netif_rx(skb) failed to send\n"); return -1; } return 0; }
그런데 이렇게 해서 보내면 ip_rcv 함수까지는 skb가 전달이 되는데, ip_rcv 후에 drop이 됩니다. 그 원인이 무엇일까 진단해 보려고 다음과 같은 jprobe, kretprobe 함수들을 만들어 ip_rcv 안에서 하는 검사 작업을 똑같이 다 해봤는데요. ip_rcv의 반환값이 1 (DROP)이 되는 것만 확인했습니다.
static void inst_ip_rcv( struct sk_buff* skb, struct net_device* dev, struct packet_type* pt ) { struct iphdr* ip_header = (struct iphdr*) (skb->nh.raw); if ( strcmp(dev->name, "lo") ) { jprobe_return(); } printk("inst_ip_rcv\n"); printk("current->comm = '%s'\n", current->comm); printk("skb->pkt_type = %u\n", skb->pkt_type); printk("ip src addr = %x\n", ntohl(ip_header->saddr) ); printk("ip dst addr = %x\n", ntohl(ip_header->daddr) ); printk("dev->name = '%s'\n", dev->name); printk("skb_share_check = %x\n", (unsigned int)skb_share_check(skb, GFP_ATOMIC)); printk("pskb_may_pull = %u\n", (unsigned int)pskb_may_pull(skb, sizeof(struct iphdr))); printk("skb->nh.iph->ihl = %u\n", skb->nh.iph->ihl); printk("skb->nh.iph->version = %u\n", skb->nh.iph->version); printk("pskb_may_pull = %u\n", (unsigned int)pskb_may_pull(skb, skb->nh.iph->ihl*4)); printk("ip_fast_csum = %u\n", ip_fast_csum((u8*)skb->nh.iph, skb->nh.iph->ihl)); printk("skb->len = %u\n", skb->len); printk("iph->tot_len = %u\n", ntohs(skb->nh.iph->tot_len)); printk("pskb_trim_rcsum = %u\n", pskb_trim_rcsum(skb, ntohs(skb->nh.iph->tot_len))); jprobe_return(); } static int inst_ip_rcv_ret( struct kretprobe_instance* ri, struct pt_regs* regs ) { int retval = regs_return_value(regs); printk("'%s' : ip_rcv returned %d\n", current->comm, retval); return 0; }
다음은 위의 검사코드가 토해내는 kernel output 값의 일부입니다.
Feb 15 14:40:01 bjlee-xnote kernel: inst_ip_rcv Feb 15 14:40:01 bjlee-xnote kernel: current->comm = 'firefox-bin' Feb 15 14:40:01 bjlee-xnote kernel: skb->pkt_type = 5 Feb 15 14:40:01 bjlee-xnote kernel: ip src addr = 7f000001 Feb 15 14:40:01 bjlee-xnote kernel: ip dst addr = 7f000001 Feb 15 14:40:01 bjlee-xnote kernel: dev->name = 'lo' Feb 15 14:40:01 bjlee-xnote kernel: skb_share_check = f41c1200 Feb 15 14:40:01 bjlee-xnote kernel: pskb_may_pull = 1 Feb 15 14:40:01 bjlee-xnote kernel: skb->nh.iph->ihl = 5 Feb 15 14:40:01 bjlee-xnote kernel: skb->nh.iph->version = 4 Feb 15 14:40:01 bjlee-xnote kernel: pskb_may_pull = 1 Feb 15 14:40:01 bjlee-xnote kernel: ip_fast_csum = 0 Feb 15 14:40:01 bjlee-xnote kernel: skb->len = 76 Feb 15 14:40:01 bjlee-xnote kernel: iph->tot_len = 76 Feb 15 14:40:01 bjlee-xnote kernel: pskb_trim_rcsum = 0 Feb 15 14:40:01 bjlee-xnote kernel: 'firefox-bin' : ip_rcv returned 1
여기까지만 보면 특별한 문제는 없어보이거든요. 그러니, 저로서는 무엇이 ip_rcv의 return값을 1로 만드는 것인지를 짐작하기가 참 어렵네요. 이 부분만 해결되면 skb가 바로 ip_rcv_finish까지 전달이 될 것 같은데 말이죠.
고수분들의 조언 부탁드립니다.
Forums:
자답입니다.
몰랐는데, 이렇게 패킷을 IP 레이어에 집어넣을때에는
소스 주소는 local주소나 loopback 주소가 되면 안되고,
목적지 주소는 loopback 주소가 되면 안되더군요. ㅜㅜ
이걸 알아내기 위해서 사흘씩이나 삽질하다니... ㅎㅎ
좀 자러 가야겠습니다.
Byungjoon Lee, http://www.buggymind.com
Byungjoon Lee, http://www.buggymind.com
와 제가 찾던 정보가 여기있따니 ㅠㅠ
감사드립니당ㅠㅠ
댓글 달기