netif_rx를 사용한 패킷 전송 관련 질문

bjlee72의 이미지

안녕하세요? 질문으로 맨 처음 인사를 드리게 되어 송구하네요. ^^;

제가 요즘 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까지 전달이 될 것 같은데 말이죠.

고수분들의 조언 부탁드립니다.

bjlee72의 이미지

몰랐는데, 이렇게 패킷을 IP 레이어에 집어넣을때에는
소스 주소는 local주소나 loopback 주소가 되면 안되고,
목적지 주소는 loopback 주소가 되면 안되더군요. ㅜㅜ

이걸 알아내기 위해서 사흘씩이나 삽질하다니... ㅎㅎ
좀 자러 가야겠습니다.

Byungjoon Lee, http://www.buggymind.com

Byungjoon Lee, http://www.buggymind.com

wlgns12www의 이미지

감사드립니당ㅠㅠ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.