关于Linux下原始套接字发送分片包的问题?

发布于 2022-10-15 07:02:28 字数 22536 浏览 17 评论 0

关于Linux下原始套接字发送分片包的问题?

如题,在Linux环境下,使用原始套接字发送数据包,

如果是普通的小于1500字节的TCP,UDP,ICMP数据包都没有问题,

但是如果是大于1500字节,程序就会分片IP包,可是问题来了

IP分片的代码都是同一个函数,但是TCP,UDP协议只能够发出第一个分片包,余下的分片包就发不出来了,

而ICMP则没有这个问题,都能够发出来,并且可以收到对方的响应

分片包都是使用sendto发送,返回值都是对的,没有错误发生。

这个问题在linux2.4.20和2.6.35.6下都是一样的,firewall都是关闭的

可是相同的代码在windows2003下运行就没有问题,

真是奇怪,大家有遇到过类似问题,知道是什么原因吗?

我查了一下linux2.6.35.6内核的代码linux+v2.6.35.6/net/ipv4/raw.c

并没有发现内核对分片包做什么特殊处理啊?大家知道什么原因吗?多谢指教,谢谢

static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
450                       size_t len)
451{
452        struct inet_sock *inet = inet_sk(sk);
453        struct ipcm_cookie ipc;
454        struct rtable *rt = NULL;
455        int free = 0;
456        __be32 daddr;
457        __be32 saddr;
458        u8  tos;
459        int err;
460
461        err = -EMSGSIZE;
462        if (len > 0xFFFF)
463                goto out;
464
465        /*
466         *      Check the flags.
467         */
468
469        err = -EOPNOTSUPP;
470        if (msg->msg_flags & MSG_OOB)   /* Mirror BSD error message */
471                goto out;               /* compatibility */
472
473        /*
474         *      Get and verify the address.
475         */
476
477        if (msg->msg_namelen) {
478                struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name;
479                err = -EINVAL;
480                if (msg->msg_namelen < sizeof(*usin))
481                        goto out;
482                if (usin->sin_family != AF_INET) {
483                        static int complained;
484                        if (!complained++)
485                                printk(KERN_INFO "%s forgot to set AF_INET in "
486                                                 "raw sendmsg. Fix it!\n",
487                                                 current->comm);
488                        err = -EAFNOSUPPORT;
489                        if (usin->sin_family)
490                                goto out;
491                }
492                daddr = usin->sin_addr.s_addr;
493                /* ANK: I did not forget to get protocol from port field.
494                 * I just do not know, who uses this weirdness.
495                 * IP_HDRINCL is much more convenient.
496                 */
497        } else {
498                err = -EDESTADDRREQ;
499                if (sk->sk_state != TCP_ESTABLISHED)
500                        goto out;
501                daddr = inet->inet_daddr;
502        }
503
504        ipc.addr = inet->inet_saddr;
505        ipc.opt = NULL;
506        ipc.shtx.flags = 0;
507        ipc.oif = sk->sk_bound_dev_if;
508
509        if (msg->msg_controllen) {
510                err = ip_cmsg_send(sock_net(sk), msg, &ipc);
511                if (err)
512                        goto out;
513                if (ipc.opt)
514                        free = 1;
515        }
516
517        saddr = ipc.addr;
518        ipc.addr = daddr;
519
520        if (!ipc.opt)
521                ipc.opt = inet->opt;
522
523        if (ipc.opt) {
524                err = -EINVAL;
525                /* Linux does not mangle headers on raw sockets,
526                 * so that IP options + IP_HDRINCL is non-sense.
527                 */
528                if (inet->hdrincl)
529                        goto done;
530                if (ipc.opt->srr) {
531                        if (!daddr)
532                                goto done;
533                        daddr = ipc.opt->faddr;
534                }
535        }
536        tos = RT_CONN_FLAGS(sk);
537        if (msg->msg_flags & MSG_DONTROUTE)
538                tos |= RTO_ONLINK;
539
540        if (ipv4_is_multicast(daddr)) {
541                if (!ipc.oif)
542                        ipc.oif = inet->mc_index;
543                if (!saddr)
544                        saddr = inet->mc_addr;
545        }
546
547        {
548                struct flowi fl = { .oif = ipc.oif,
549                                    .mark = sk->sk_mark,
550                                    .nl_u = { .ip4_u =
551                                              { .daddr = daddr,
552                                                .saddr = saddr,
553                                                .tos = tos } },
554                                    .proto = inet->hdrincl ? IPPROTO_RAW :
555                                                             sk->sk_protocol,
556                                  };
557                if (!inet->hdrincl) {
558                        err = raw_probe_proto_opt(&fl, msg);
559                        if (err)
560                                goto done;
561                }
562
563                security_sk_classify_flow(sk, &fl);
564                err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 1);
565        }
566        if (err)
567                goto done;
568
569        err = -EACCES;
570        if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST))
571                goto done;
572
573        if (msg->msg_flags & MSG_CONFIRM)
574                goto do_confirm;
575back_from_confirm:
576
577        if (inet->hdrincl)
578                err = raw_send_hdrinc(sk, msg->msg_iov, len,
579                                        rt, msg->msg_flags);
580
581         else {
582                if (!ipc.addr)
583                        ipc.addr = rt->rt_dst;
584                lock_sock(sk);
585                err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0,
586                                        &ipc, &rt, msg->msg_flags);
587                if (err)
588                        ip_flush_pending_frames(sk);
589                else if (!(msg->msg_flags & MSG_MORE)) {
590                        err = ip_push_pending_frames(sk);
591                        if (err == -ENOBUFS && !inet->recverr)
592                                err = 0;
593                }
594                release_sock(sk);
595        }
596done:
597        if (free)
598                kfree(ipc.opt);
599        ip_rt_put(rt);
600
601out:
602        if (err < 0)
603                return err;
604        return len;
605
606do_confirm:
607        dst_confirm(&rt->u.dst);
608        if (!(msg->msg_flags & MSG_PROBE) || len)
609                goto back_from_confirm;
610        err = 0;
611        goto done;
612}

static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
317                        struct rtable *rt,
318                        unsigned int flags)
319{
320        struct inet_sock *inet = inet_sk(sk);
321        struct net *net = sock_net(sk);
322        struct iphdr *iph;
323        struct sk_buff *skb;
324        unsigned int iphlen;
325        int err;
326
327        if (length > rt->u.dst.dev->mtu) {
328                ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport,
329                               rt->u.dst.dev->mtu);
330                return -EMSGSIZE;
331        }
332        if (flags&MSG_PROBE)
333                goto out;
334
335        skb = sock_alloc_send_skb(sk,
336                                  length + LL_ALLOCATED_SPACE(rt->u.dst.dev) + 15,
337                                  flags & MSG_DONTWAIT, &err);
338        if (skb == NULL)
339                goto error;
340        skb_reserve(skb, LL_RESERVED_SPACE(rt->u.dst.dev));
341
342        skb->priority = sk->sk_priority;
343        skb->mark = sk->sk_mark;
344        skb_dst_set(skb, dst_clone(&rt->u.dst));
345
346        skb_reset_network_header(skb);
347        iph = ip_hdr(skb);
348        skb_put(skb, length);
349
350        skb->ip_summed = CHECKSUM_NONE;
351
352        skb->transport_header = skb->network_header;
353        err = -EFAULT;
354        if (memcpy_fromiovecend((void *)iph, from, 0, length))
355                goto error_free;
356
357        iphlen = iph->ihl * 4;
358
359        /*
360         * We don't want to modify the ip header, but we do need to
361         * be sure that it won't cause problems later along the network
362         * stack.  Specifically we want to make sure that iph->ihl is a
363         * sane value.  If ihl points beyond the length of the buffer passed
364         * in, reject the frame as invalid
365         */
366        err = -EINVAL;
367        if (iphlen > length)
368                goto error_free;
369
370        if (iphlen >= sizeof(*iph)) {
371                if (!iph->saddr)
372                        iph->saddr = rt->rt_src;
373                iph->check   = 0;
374                iph->tot_len = htons(length);
375                if (!iph->id)
376                        ip_select_ident(iph, &rt->u.dst, NULL);
377
378                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
379        }
380        if (iph->protocol == IPPROTO_ICMP)
381                icmp_out_count(net, ((struct icmphdr *)
382                        skb_transport_header(skb))->type);
383
384        err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
385                      rt->u.dst.dev, dst_output);
386        if (err > 0)
387                err = net_xmit_errno(err);
388        if (err)
389                goto error;
390out:
391        return 0;
392
393error_free:
394        kfree_skb(skb);
395error:
396        IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
397        if (err == -ENOBUFS && !inet->recverr)
398                err = 0;
399        return err;
400}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小猫一只 2022-10-22 07:02:28

顶一下,顺带问一下,这种问题发在哪个版块比较好?貌似这个版块的人都不大感兴趣哈

迎风吟唱 2022-10-22 07:02:28

顶顶更健康:wink:

友欢 2022-10-22 07:02:28

莫非CU的人,不上班就不上网么?

那支青花 2022-10-22 07:02:28

你是抓包看的只能发出一个分片还是用你的代码看只收到了一个分片?

带刺的爱情 2022-10-22 07:02:28

是的,都是使用wireshark抓包看到的,也就是说,IP头部偏移量大于0的,都不能发出来,怪异得很

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文