linux内核发送构造数据包的方式,Linux内核发送构造数据包的方式

/* Send RST reply */

static void send_reset(struct sk_buff *oldskb, int hook)

{

struct sk_buff *nskb;

struct iphdr *iph = oldskb->nh.iph;

struct tcphdr _otcph, *oth, *tcph;

struct rtable *rt;

u_int16_t tmp_port;

u_int32_t tmp_addr;

int needs_ack;

int hh_len;

/* 判断是否是分片包*/

if (oldskb->nh.iph->frag_off & htons(IP_OFFSET))

return;

/*得到TCP头部指针*/

oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4,

sizeof(_otcph), &_otcph);

if (oth == NULL)

return;

/* 当期收到的包就是RST包,就不用再发送RST包了*/

if (oth->rst)

return;

/*检查数据包的校验和是否正确*/

if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP))

return;

/*这一步比较关键,做的就是更新路由的工作。该函数的主要工作就是将当前数据包的源IP当做路由的目的IP,同时考虑数据包的目的IP,得到去往该源IP的路由*/

if ((rt = route_reverse(oldskb, oth, hook)) == NULL)

return;

hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);

/* 拷贝当前的oldskb,包括skb结构体和数据部分。这就是我们上面提到的构造数据包的第一种方式*/

nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),

GFP_ATOMIC);

if (!nskb) {

dst_release(&rt->u.dst);

return;

}

/*因为是拷贝的oldskb,这里不需要再引用了,因此释放对该路由项的引用*/

dst_release(nskb->dst);

/*将新构造数据包引用的路由指向上面由route_reverse函数返回的新的路由项 */

nskb->dst = &rt->u.dst;

/* 清除nskb中拷贝过来的oldskb中链接跟踪相关的内容*/

nf_reset(nskb);

nskb->nfmark = 0;

skb_init_secmark(nskb);

/*以下就是构造数据包的实际数据部分。如果我们将这里不为nskb新申请缓冲区,而直接指向oldskb的缓冲区,就使我们上面提到的第二种构造数据包的方法。*/

/*获取nskb的tcp header*/

tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);

/*交换源和目的IP */

tmp_addr = nskb->nh.iph->saddr;

nskb->nh.iph->saddr = nskb->nh.iph->daddr;

nskb->nh.iph->daddr = tmp_addr;

/*交换源和目的端口 */

tmp_port = tcph->source;

tcph->source = tcph->dest;

tcph->dest = tmp_port;

/*重置TCP头部的长度,并修改IP头部中记录的数据包的总长度。因为这里是发送RST报文,只需要有TCP的头部,不需要TCP的数据部分*/

tcph->doff = sizeof(struct tcphdr)/4;

skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));

nskb->nh.iph->tot_len = htons(nskb->len);

/*重新设置 seq, ack_seq,分两种情况(TCP/IP详解有描述)*/

if (tcph->ack) { /*原始数据包中ACK标记位置位的情况*/

needs_ack = 0;

tcph->seq = oth->ack_seq; /*原始数据包的ack_seq作为nskb的seq*/

tcph->ack_seq = 0;

} else { /*原始数据包中ACK标记位没有置位的情况,初始连接SYN或者结束连接FIN等*/

needs_ack = 1;

/*这种情况应该是SYN或者FIN包,由于SYN和FIN包都占用1个字节的长度。因此ack_seq应该等于旧包的seq+1即可。这里之所以这样表示,可能是还存在其他情况的数据包。*/

tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin

+ oldskb->len - oldskb->nh.iph->ihl*4

- (oth->doff<<2));

tcph->seq = 0;

}

/* RST标记位置1*/

((u_int8_t *)tcph)[13] = 0;

tcph->rst = 1;

tcph->ack = needs_ack;

tcph->window = 0;

tcph->urg_ptr = 0;

/*重新计算TCP校验和*/

tcph->check = 0;

tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),

nskb->nh.iph->saddr,

nskb->nh.iph->daddr,

csum_partial((char *)tcph,

sizeof(struct tcphdr), 0));

/* 修改IP包的TTL,并且设置禁止分片*/

nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);

/* Set DF, id = 0 */

nskb->nh.iph->frag_off = htons(IP_DF);

nskb->nh.iph->id = 0;

/*重新计算IP数据包头部校验和*/

nskb->nh.iph->check = 0;

nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,

nskb->nh.iph->ihl);

/* "Never happens" */

if (nskb->len > dst_mtu(nskb->dst))

goto free_nskb;

/*使nskb和oldskb的链接记录关联*/

nf_ct_attach(nskb, oldskb);

/*这里就是最终发送数据包的方式,具体方法就是让新数据包经过LOACLOUT的hook点,然后查路由,最后经由POSTROUTING点,将数据包发送出去。

其实这里我还是有1个疑问:(1)为什么不可以直接查找路由,而必须先经过LOCALOUT点*/

NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,

dst_output);

return;

free_nskb:

kfree_skb(nskb);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值