수신 받은 패킷을 체크하여 다른 인터페이스로 전송하는 방법좀 알려주세요.

julggol의 이미지

관심 가져 주셔서 감사합니다.
며칠째 계속 이거 하나 때문에 고생하고 있습니다. 좀 도와 주세요.
디바이스 드라이버에서 패킷을 수신 받으면 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;
}
sjpark의 이미지

bptable 이 아니고
ebtable 이었어요

원하시는게, 하나의 PC에 1개의 외부 망과 연결된 NIC 가 있고, n 개의 내부망 NIC가 있는 경우,
외부망으로 부터 들어오는 Packet 들 중 일부를 내부망의 특정 PC로 바로 포워딩 시켜버리는 것이 맞다면
ebtables 이 맞을거에요.

중간 브리지 서버는 단순히 내부망으로 들어가는 모든 패킷들을 감시할 수 있고, 필요하다면 여러가지 패킷 작업을 할 수 도 있겠죠.

http://ebtables.sourceforge.net/

사이트는 위이고요

원하시는것이 맞기를 바랍니다.

그럼 고운 하루 보내세요.

http://nicesj.com

julggol의 이미지

아~ ebtables 였군요...
자세히는 못봤지만 아마도 가능할것 같네요.
계속 신경써 주셔서 감사합니다.
ebtables 분석좀 하고 결과 올리겠습니다.
수고하세요.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.