📄 tcp_ipv4.c
字号:
bh_unlock_sock(sk); sock_put(sk);}/* This routine computes an IPv4 TCP checksum. */void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb){ struct inet_sock *inet = inet_sk(sk); struct tcphdr *th = tcp_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { th->check = ~tcp_v4_check(len, inet->saddr, inet->daddr, 0); skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); } else { th->check = tcp_v4_check(len, inet->saddr, inet->daddr, csum_partial((char *)th, th->doff << 2, skb->csum)); }}int tcp_v4_gso_send_check(struct sk_buff *skb){ const struct iphdr *iph; struct tcphdr *th; if (!pskb_may_pull(skb, sizeof(*th))) return -EINVAL; iph = ip_hdr(skb); th = tcp_hdr(skb); th->check = 0; th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0); skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; return 0;}/* * This routine will send an RST to the other tcp. * * Someone asks: why I NEVER use socket parameters (TOS, TTL etc.) * for reset. * Answer: if a packet caused RST, it is not for a socket * existing in our system, if it is matched to a socket, * it is just duplicate segment or bug in other side's TCP. * So that we build reply only basing on parameters * arrived with segment. * Exception: precedence violation. We do not implement it in any case. */static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb){ struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th;#ifdef CONFIG_TCP_MD5SIG __be32 opt[(TCPOLEN_MD5SIG_ALIGNED >> 2)];#endif } rep; struct ip_reply_arg arg;#ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key;#endif /* Never send a reset in response to a reset. */ if (th->rst) return; if (((struct rtable *)skb->dst)->rt_type != RTN_LOCAL) return; /* Swap the send and the receive. */ memset(&rep, 0, sizeof(rep)); rep.th.dest = th->source; rep.th.source = th->dest; rep.th.doff = sizeof(struct tcphdr) / 4; rep.th.rst = 1; if (th->ack) { rep.th.seq = th->ack_seq; } else { rep.th.ack = 1; rep.th.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2)); } memset(&arg, 0, sizeof(arg)); arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th);#ifdef CONFIG_TCP_MD5SIG key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL; if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); /* Update length and the length the header thinks exists */ arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED; rep.th.doff = arg.iov[0].iov_len / 4; tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1], key, ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, &rep.th, IPPROTO_TCP, arg.iov[0].iov_len); }#endif arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, /* XXX */ sizeof(struct tcphdr), IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);}/* The code following below sending ACKs in SYN-RECV and TIME-WAIT states outside socket context is ugly, certainly. What can I do? */static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts){ struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th; __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)#ifdef CONFIG_TCP_MD5SIG + (TCPOLEN_MD5SIG_ALIGNED >> 2)#endif ]; } rep; struct ip_reply_arg arg;#ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key; struct tcp_md5sig_key tw_key;#endif memset(&rep.th, 0, sizeof(struct tcphdr)); memset(&arg, 0, sizeof(arg)); arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); if (ts) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); rep.opt[1] = htonl(tcp_time_stamp); rep.opt[2] = htonl(ts); arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED; } /* Swap the send and the receive. */ rep.th.dest = th->source; rep.th.source = th->dest; rep.th.doff = arg.iov[0].iov_len / 4; rep.th.seq = htonl(seq); rep.th.ack_seq = htonl(ack); rep.th.ack = 1; rep.th.window = htons(win);#ifdef CONFIG_TCP_MD5SIG /* * The SKB holds an imcoming packet, but may not have a valid ->sk * pointer. This is especially the case when we're dealing with a * TIME_WAIT ack, because the sk structure is long gone, and only * the tcp_timewait_sock remains. So the md5 key is stashed in that * structure, and we use it in preference. I believe that (twsk || * skb->sk) holds true, but we program defensively. */ if (!twsk && skb->sk) { key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr); } else if (twsk && twsk->tw_md5_keylen) { tw_key.key = twsk->tw_md5_key; tw_key.keylen = twsk->tw_md5_keylen; key = &tw_key; } else key = NULL; if (key) { int offset = (ts) ? 3 : 0; rep.opt[offset++] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED; rep.th.doff = arg.iov[0].iov_len/4; tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset], key, ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, &rep.th, IPPROTO_TCP, arg.iov[0].iov_len); }#endif arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, /* XXX */ arg.iov[0].iov_len, IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; if (twsk) arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if; ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);}static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb){ struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcptw->tw_ts_recent); inet_twsk_put(tw);}static void tcp_v4_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req){ tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent);}/* * Send a SYN-ACK after having received an ACK. * This still operates on a request_sock only, not on a big * socket. */static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req, struct dst_entry *dst){ const struct inet_request_sock *ireq = inet_rsk(req); int err = -1; struct sk_buff * skb; /* First, grab a route. */ if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL) goto out; skb = tcp_make_synack(sk, dst, req); if (skb) { struct tcphdr *th = tcp_hdr(skb); th->check = tcp_v4_check(skb->len, ireq->loc_addr, ireq->rmt_addr, csum_partial((char *)th, skb->len, skb->csum)); err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, ireq->rmt_addr, ireq->opt); err = net_xmit_eval(err); }out: dst_release(dst); return err;}/* * IPv4 request_sock destructor. */static void tcp_v4_reqsk_destructor(struct request_sock *req){ kfree(inet_rsk(req)->opt);}#ifdef CONFIG_SYN_COOKIESstatic void syn_flood_warning(struct sk_buff *skb){ static unsigned long warntime; if (time_after(jiffies, (warntime + HZ * 60))) { warntime = jiffies; printk(KERN_INFO "possible SYN flooding on port %d. Sending cookies.\n", ntohs(tcp_hdr(skb)->dest)); }}#endif/* * Save and compile IPv4 options into the request_sock if needed. */static struct ip_options *tcp_v4_save_options(struct sock *sk, struct sk_buff *skb){ struct ip_options *opt = &(IPCB(skb)->opt); struct ip_options *dopt = NULL; if (opt && opt->optlen) { int opt_size = optlength(opt); dopt = kmalloc(opt_size, GFP_ATOMIC); if (dopt) { if (ip_options_echo(dopt, skb)) { kfree(dopt); dopt = NULL; } } } return dopt;}#ifdef CONFIG_TCP_MD5SIG/* * RFC2385 MD5 checksumming requires a mapping of * IP address->MD5 Key. * We need to maintain these in the sk structure. *//* Find the Key structure for an address. */static struct tcp_md5sig_key * tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr){ struct tcp_sock *tp = tcp_sk(sk); int i; if (!tp->md5sig_info || !tp->md5sig_info->entries4) return NULL; for (i = 0; i < tp->md5sig_info->entries4; i++) { if (tp->md5sig_info->keys4[i].addr == addr) return &tp->md5sig_info->keys4[i].base; } return NULL;}struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, struct sock *addr_sk){ return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->daddr);}EXPORT_SYMBOL(tcp_v4_md5_lookup);static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, struct request_sock *req){ return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr);}/* This can be called on a newly created socket, from other files */int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, u8 *newkey, u8 newkeylen){ /* Add Key to the list */ struct tcp_md5sig_key *key; struct tcp_sock *tp = tcp_sk(sk); struct tcp4_md5sig_key *keys; key = tcp_v4_md5_do_lookup(sk, addr); if (key) { /* Pre-existing entry - just update that one. */ kfree(key->key); key->key = newkey; key->keylen = newkeylen; } else { struct tcp_md5sig_info *md5sig; if (!tp->md5sig_info) { tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC); if (!tp->md5sig_info) { kfree(newkey); return -ENOMEM; } sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } if (tcp_alloc_md5sig_pool() == NULL) { kfree(newkey); return -ENOMEM; } md5sig = tp->md5sig_info; if (md5sig->alloced4 == md5sig->entries4) { keys = kmalloc((sizeof(*keys) * (md5sig->entries4 + 1)), GFP_ATOMIC); if (!keys) { kfree(newkey); tcp_free_md5sig_pool(); return -ENOMEM; } if (md5sig->entries4) memcpy(keys, md5sig->keys4, sizeof(*keys) * md5sig->entries4); /* Free old key list, and reference new one */ kfree(md5sig->keys4); md5sig->keys4 = keys; md5sig->alloced4++; } md5sig->entries4++; md5sig->keys4[md5sig->entries4 - 1].addr = addr; md5sig->keys4[md5sig->entries4 - 1].base.key = newkey; md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen; } return 0;}EXPORT_SYMBOL(tcp_v4_md5_do_add);static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk, u8 *newkey, u8 newkeylen){ return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->daddr, newkey, newkeylen);}int tcp_v4_md5_do_del(struct sock *sk, __be32 addr){ struct tcp_sock *tp = tcp_sk(sk); int i; for (i = 0; i < tp->md5sig_info->entries4; i++) { if (tp->md5sig_info->keys4[i].addr == addr) { /* Free the key */ kfree(tp->md5sig_info->keys4[i].base.key); tp->md5sig_info->entries4--; if (tp->md5sig_info->entries4 == 0) { kfree(tp->md5sig_info->keys4); tp->md5sig_info->keys4 = NULL; tp->md5sig_info->alloced4 = 0; } else if (tp->md5sig_info->entries4 != i) { /* Need to do some manipulation */ memmove(&tp->md5sig_info->keys4[i], &tp->md5sig_info->keys4[i+1], (tp->md5sig_info->entries4 - i) * sizeof(struct tcp4_md5sig_key)); } tcp_free_md5sig_pool(); return 0; } } return -ENOENT;}EXPORT_SYMBOL(tcp_v4_md5_do_del);static void tcp_v4_clear_md5_list(struct sock *sk){ struct tcp_sock *tp = tcp_sk(sk); /* Free each key, then the set of key keys, * the crypto element, and then decrement our * hold on the last resort crypto. */ if (tp->md5sig_info->entries4) { int i; for (i = 0; i < tp->md5sig_info->entries4; i++) kfree(tp->md5sig_info->keys4[i].base.key); tp->md5sig_info->entries4 = 0; tcp_free_md5sig_pool(); } if (tp->md5sig_info->keys4) { kfree(tp->md5sig_info->keys4); tp->md5sig_info->keys4 = NULL; tp->md5sig_info->alloced4 = 0; }}static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, int optlen){ struct tcp_md5sig cmd; struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; if (copy_from_user(&cmd, optval, sizeof(cmd))) return -EFAULT; if (sin->sin_family != AF_INET) return -EINVAL; if (!cmd.tcpm_key || !cmd.tcpm_keylen) { if (!tcp_sk(sk)->md5sig_info)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -