📄 tcp_ipv6.c
字号:
static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req){ tcp_v6_send_ack(skb, req->snt_isn+1, req->rcv_isn+1, req->rcv_wnd, req->ts_recent);}static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb){ struct open_request *req, **prev; struct tcphdr *th = skb->h.th; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sock *nsk; /* Find possible connection requests. */ req = tcp_v6_search_req(tp, skb->nh.ipv6h, th, tcp_v6_iif(skb), &prev); if (req) return tcp_check_req(sk, skb, req, prev); nsk = __tcp_v6_lookup_established(&skb->nh.ipv6h->saddr, th->source, &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb)); if (nsk) { if (nsk->state != TCP_TIME_WAIT) { bh_lock_sock(nsk); return nsk; } tcp_tw_put((struct tcp_tw_bucket*)nsk); return NULL; }#if 0 /*def CONFIG_SYN_COOKIES*/ if (!th->rst && !th->syn && th->ack) sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt));#endif return sk;}static void tcp_v6_synq_add(struct sock *sk, struct open_request *req){ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; struct tcp_listen_opt *lopt = tp->listen_opt; unsigned h = tcp_v6_synq_hash(&req->af.v6_req.rmt_addr, req->rmt_port); req->sk = NULL; req->expires = jiffies + TCP_TIMEOUT_INIT; req->retrans = 0; req->index = h; req->dl_next = lopt->syn_table[h]; write_lock(&tp->syn_wait_lock); lopt->syn_table[h] = req; write_unlock(&tp->syn_wait_lock); tcp_synq_added(sk);}/* FIXME: this is substantially similar to the ipv4 code. * Can some kind of merge be done? -- erics */static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb){ struct tcp_opt tp; struct open_request *req = NULL; __u32 isn = TCP_SKB_CB(skb)->when; if (skb->protocol == __constant_htons(ETH_P_IP)) return tcp_v4_conn_request(sk, skb); /* FIXME: do the same check for anycast */ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) goto drop; /* * There are no SYN attacks on IPv6, yet... */ if (tcp_synq_is_full(sk) && !isn) { if (net_ratelimit()) printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n"); goto drop; } if (tcp_acceptq_is_full(sk) && tcp_synq_young(sk) > 1) goto drop; req = tcp_openreq_alloc(); if (req == NULL) goto drop; tcp_clear_options(&tp); tp.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); tp.user_mss = sk->tp_pinfo.af_tcp.user_mss; tcp_parse_options(skb, &tp, 0); tp.tstamp_ok = tp.saw_tstamp; tcp_openreq_init(req, &tp, skb); req->class = &or_ipv6; ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr); ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr); TCP_ECN_create_request(req, skb->h.th); req->af.v6_req.pktopts = NULL; if (ipv6_opt_accepted(sk, skb) || sk->net_pinfo.af_inet6.rxopt.bits.rxinfo || sk->net_pinfo.af_inet6.rxopt.bits.rxhlim) { atomic_inc(&skb->users); req->af.v6_req.pktopts = skb; } req->af.v6_req.iif = sk->bound_dev_if; /* So that link locals have meaning */ if (!sk->bound_dev_if && ipv6_addr_type(&req->af.v6_req.rmt_addr)&IPV6_ADDR_LINKLOCAL) req->af.v6_req.iif = tcp_v6_iif(skb); if (isn == 0) isn = tcp_v6_init_sequence(sk,skb); req->snt_isn = isn; if (tcp_v6_send_synack(sk, req, NULL)) goto drop; tcp_v6_synq_add(sk, req); return 0;drop: if (req) tcp_openreq_free(req); TCP_INC_STATS_BH(TcpAttemptFails); return 0; /* don't send reset */}static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, struct dst_entry *dst){ struct ipv6_pinfo *np; struct flowi fl; struct tcp_opt *newtp; struct sock *newsk; struct ipv6_txoptions *opt; if (skb->protocol == __constant_htons(ETH_P_IP)) { /* * v6 mapped */ newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst); if (newsk == NULL) return NULL; np = &newsk->net_pinfo.af_inet6; ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF), newsk->daddr); ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF), newsk->saddr); ipv6_addr_copy(&np->rcv_saddr, &np->saddr); newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped; newsk->backlog_rcv = tcp_v4_do_rcv; newsk->net_pinfo.af_inet6.pktoptions = NULL; newsk->net_pinfo.af_inet6.opt = NULL; newsk->net_pinfo.af_inet6.mcast_oif = tcp_v6_iif(skb); newsk->net_pinfo.af_inet6.mcast_hops = skb->nh.ipv6h->hop_limit; /* Charge newly allocated IPv6 socket. Though it is mapped, * it is IPv6 yet. */#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr);#endif MOD_INC_USE_COUNT; /* It is tricky place. Until this moment IPv4 tcp worked with IPv6 af_tcp.af_specific. Sync it now. */ tcp_sync_mss(newsk, newsk->tp_pinfo.af_tcp.pmtu_cookie); return newsk; } opt = sk->net_pinfo.af_inet6.opt; if (tcp_acceptq_is_full(sk)) goto out_overflow; if (sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && opt == NULL && req->af.v6_req.pktopts) { struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)req->af.v6_req.pktopts->cb; if (rxopt->srcrt) opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(req->af.v6_req.pktopts->nh.raw+rxopt->srcrt)); } if (dst == NULL) { fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr; if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; fl.nl_u.ip6_u.daddr = rt0->addr; } fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr; fl.fl6_flowlabel = 0; fl.oif = sk->bound_dev_if; fl.uli_u.ports.dport = req->rmt_port; fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); } if (dst->error) goto out; newsk = tcp_create_openreq_child(sk, req, skb); if (newsk == NULL) goto out; /* Charge newly allocated IPv6 socket */#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr);#endif MOD_INC_USE_COUNT; ip6_dst_store(newsk, dst, NULL); sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; newtp = &(newsk->tp_pinfo.af_tcp); np = &newsk->net_pinfo.af_inet6; ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr); ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr); ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); newsk->bound_dev_if = req->af.v6_req.iif; /* Now IPv6 options... First: no IPv4 options. */ newsk->protinfo.af_inet.opt = NULL; /* Clone RX bits */ np->rxopt.all = sk->net_pinfo.af_inet6.rxopt.all; /* Clone pktoptions received with SYN */ np->pktoptions = NULL; if (req->af.v6_req.pktopts) { np->pktoptions = skb_clone(req->af.v6_req.pktopts, GFP_ATOMIC); kfree_skb(req->af.v6_req.pktopts); req->af.v6_req.pktopts = NULL; if (np->pktoptions) skb_set_owner_r(np->pktoptions, newsk); } np->opt = NULL; np->mcast_oif = tcp_v6_iif(skb); np->mcast_hops = skb->nh.ipv6h->hop_limit; /* Clone native IPv6 options from listening socket (if any) Yes, keeping reference count would be much more clever, but we make one more one thing there: reattach optmem to newsk. */ if (opt) { np->opt = ipv6_dup_options(newsk, opt); if (opt != sk->net_pinfo.af_inet6.opt) sock_kfree_s(sk, opt, opt->tot_len); } newtp->ext_header_len = 0; if (np->opt) newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; tcp_sync_mss(newsk, dst->pmtu); newtp->advmss = dst->advmss; tcp_initialize_rcv_mss(newsk); newsk->daddr = LOOPBACK4_IPV6; newsk->saddr = LOOPBACK4_IPV6; newsk->rcv_saddr= LOOPBACK4_IPV6; __tcp_v6_hash(newsk); tcp_inherit_port(sk, newsk); return newsk;out_overflow: NET_INC_STATS_BH(ListenOverflows);out: NET_INC_STATS_BH(ListenDrops); if (opt && opt != sk->net_pinfo.af_inet6.opt) sock_kfree_s(sk, opt, opt->tot_len); dst_release(dst); return NULL;}static int tcp_v6_checksum_init(struct sk_buff *skb){ if (skb->ip_summed == CHECKSUM_HW) { skb->ip_summed = CHECKSUM_UNNECESSARY; if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,skb->csum)) return 0; NETDEBUG(if (net_ratelimit()) printk(KERN_DEBUG "hw tcp v6 csum failed\n")); } if (skb->len <= 76) { if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0))) return -1; skb->ip_summed = CHECKSUM_UNNECESSARY; } else { skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,0); } return 0;}/* The socket must have it's spinlock held when we get * here. * * We have a potential double-lock case here, so even when * doing backlog processing we use the BH locking scheme. * This is because we cannot sleep with the original spinlock * held. */static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb){#ifdef CONFIG_FILTER struct sk_filter *filter;#endif struct sk_buff *opt_skb = NULL; /* Imagine: socket is IPv6. IPv4 packet arrives, goes to IPv4 receive handler and backlogged. From backlog it always goes here. Kerboom... Fortunately, tcp_rcv_established and rcv_established handle them correctly, but it is not case with tcp_v6_hnd_req and tcp_v6_send_reset(). --ANK */ if (skb->protocol == __constant_htons(ETH_P_IP)) return tcp_v4_do_rcv(sk, skb);#ifdef CONFIG_FILTER filter = sk->filter; if (filter && sk_filter(skb, filter)) goto discard;#endif /* CONFIG_FILTER */ /* * socket locking is here for SMP purposes as backlog rcv * is currently called with bh processing disabled. */ IP6_INC_STATS_BH(Ip6InDelivers); /* Do Stevens' IPV6_PKTOPTIONS. Yes, guys, it is the only place in our code, where we may make it not affecting IPv4. The rest of code is protocol independent, and I do not like idea to uglify IPv4. Actually, all the idea behind IPV6_PKTOPTIONS looks not very well thought. For now we latch options, received in the last packet, enqueued by tcp. Feel free to propose better solution. --ANK (980728) */ if (sk->net_pinfo.af_inet6.rxopt.all) opt_skb = skb_clone(skb, GFP_ATOMIC); if (sk->state == TCP_ESTABLISHED) { /* Fast path */ TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) goto reset; TCP_CHECK_TIMER(sk); if (opt_skb) goto ipv6_pktoptions; return 0; } if (skb->len < (skb->h.th->doff<<2) || tcp_checksum_complete(skb)) goto csum_err; if (sk->state == TCP_LISTEN) { struct sock *nsk = tcp_v6_hnd_req(sk, skb); if (!nsk) goto discard; /* * Queue it on the new socket if the new socket is active, * otherwise we just shortcircuit this and continue with * the new socket.. */ if(nsk != sk) { if (tcp_child_process(sk, nsk, skb)) goto reset; if (opt_skb) __kfree_skb(opt_skb); return 0; } } TCP_CHECK_TIMER(sk); if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) goto reset; TCP_CHECK_TIMER(sk); if (opt_skb) goto ipv6_pktoptions; return 0;reset: tcp_v6_send_reset(skb);discard: if (opt_skb) __kfree_skb(opt_skb); kfree_skb(skb); return 0;csum_err: TCP_INC_STATS_BH(TcpInErrs); goto discard;ipv6_pktoptions: /* Do you ask, what is it? 1. skb was enqueued by tcp. 2. skb is added to tail of read queue, rather than out of order. 3. socket is not in passive state. 4. Finally, it really contains options, which user wants to receive. */ if (TCP_SKB_CB(opt_skb)->end_seq == sk->tp_pinfo.af_tcp.rcv_nxt && !((1<<sk->state)&(TCPF_CLOSE|TCPF_LISTEN))) { if (sk->net_pinfo.af_inet6.rxopt.bits.rxinfo) sk->net_pinfo.af_inet6.mcast_oif = tcp_v6_iif(opt_skb); if (sk->net_pinfo.af_inet6.rxopt.bits.rxhlim) sk->net_pinfo.af_inet6.mcast_hops = opt_skb->nh.ipv6h->hop_limit; if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); opt_skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, opt_skb); } else { __kfree_skb(opt_skb); opt_skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL); } } if (opt_skb) kfree_skb(opt_skb); return 0;}int tcp_v6_rcv(struct sk_buff *skb){ struct tcphdr *th; struct sock *sk; int ret; if (skb->pkt_type != PACKET_HOST) goto discard_it; /* * Count it even if it's bad. */ TCP_INC_STATS_BH(TcpInSegs); if (!pskb_may_pull(skb, sizeof(struct tcphdr))) goto discard_it; th = skb->h.th; if (th->doff < sizeof(struct tcphdr)/4) goto bad_packet; if (!pskb_may_pull(skb, th->doff*4)) goto discard_it; if ((skb->ip_summed != CHECKSUM_UNNECESSARY && tcp_v6_checksum_init(skb) < 0)) goto bad_packet; th = skb->h.th; TCP_SKB_CB(skb)->seq = ntohl(th->seq); TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff*4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); TCP_SKB_CB(skb)->when = 0; TCP_SKB_CB(skb)->flags = ip6_get_dsfield(skb->nh.ipv6h); TCP_SKB_CB(skb)->sacked = 0; sk = __tcp_v6_lookup(&skb->nh.ipv6h->saddr, th->source, &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb)); if (!sk) goto no_tcp_socket;process: if(!ipsec_sk_policy(sk,skb)) goto discard_and_relse; if(sk->state == TCP_TIME_WAIT) goto do_time_wait; skb->dev = NULL; bh_lock_sock(sk); ret = 0; if (!sk->lock.users) { if (!tcp_prequeue(sk, skb)) ret = tcp_v6_do_rcv(sk, skb); } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); sock_put(sk); return ret;no_tcp_socket: if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {bad_packet: TCP_INC_STATS_BH(TcpInErrs); } else { tcp_v6_send_reset(skb); }discard_it: /* * Discard frame */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -