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
와 제가 찾던 정보가 여기있따니 ㅠㅠ
감사드립니당ㅠㅠ
댓글 달기