📄 tcp_ipv6.c
字号:
}static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, struct sock *addr_sk){ return tcp_v6_md5_do_lookup(sk, &inet6_sk(addr_sk)->daddr);}static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, struct request_sock *req){ return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);}static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer, char *newkey, u8 newkeylen){ /* Add key to the list */ struct tcp_md5sig_key *key; struct tcp_sock *tp = tcp_sk(sk); struct tcp6_md5sig_key *keys; key = tcp_v6_md5_do_lookup(sk, peer); if (key) { /* modify existing entry - just update that one */ kfree(key->key); key->key = newkey; key->keylen = newkeylen; } else { /* reallocate new list if current one is full. */ 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; } if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) { keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) * (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC); if (!keys) { tcp_free_md5sig_pool(); kfree(newkey); return -ENOMEM; } if (tp->md5sig_info->entries6) memmove(keys, tp->md5sig_info->keys6, (sizeof (tp->md5sig_info->keys6[0]) * tp->md5sig_info->entries6)); kfree(tp->md5sig_info->keys6); tp->md5sig_info->keys6 = keys; tp->md5sig_info->alloced6++; } ipv6_addr_copy(&tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr, peer); tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey; tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen; tp->md5sig_info->entries6++; } return 0;}static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk, u8 *newkey, __u8 newkeylen){ return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr, newkey, newkeylen);}static int tcp_v6_md5_do_del(struct sock *sk, struct in6_addr *peer){ struct tcp_sock *tp = tcp_sk(sk); int i; for (i = 0; i < tp->md5sig_info->entries6; i++) { if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, peer) == 0) { /* Free the key */ kfree(tp->md5sig_info->keys6[i].base.key); tp->md5sig_info->entries6--; if (tp->md5sig_info->entries6 == 0) { kfree(tp->md5sig_info->keys6); tp->md5sig_info->keys6 = NULL; tp->md5sig_info->alloced6 = 0; } else { /* shrink the database */ if (tp->md5sig_info->entries6 != i) memmove(&tp->md5sig_info->keys6[i], &tp->md5sig_info->keys6[i+1], (tp->md5sig_info->entries6 - i) * sizeof (tp->md5sig_info->keys6[0])); } tcp_free_md5sig_pool(); return 0; } } return -ENOENT;}static void tcp_v6_clear_md5_list (struct sock *sk){ struct tcp_sock *tp = tcp_sk(sk); int i; if (tp->md5sig_info->entries6) { for (i = 0; i < tp->md5sig_info->entries6; i++) kfree(tp->md5sig_info->keys6[i].base.key); tp->md5sig_info->entries6 = 0; tcp_free_md5sig_pool(); } kfree(tp->md5sig_info->keys6); tp->md5sig_info->keys6 = NULL; tp->md5sig_info->alloced6 = 0; if (tp->md5sig_info->entries4) { 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(); } kfree(tp->md5sig_info->keys4); tp->md5sig_info->keys4 = NULL; tp->md5sig_info->alloced4 = 0;}static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, int optlen){ struct tcp_md5sig cmd; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; if (copy_from_user(&cmd, optval, sizeof(cmd))) return -EFAULT; if (sin6->sin6_family != AF_INET6) return -EINVAL; if (!cmd.tcpm_keylen) { if (!tcp_sk(sk)->md5sig_info) return -ENOENT; if (ipv6_addr_v4mapped(&sin6->sin6_addr)) return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); } if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; if (!tcp_sk(sk)->md5sig_info) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_info *p; p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL); if (!p) return -ENOMEM; tp->md5sig_info = p; sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); if (!newkey) return -ENOMEM; if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3], newkey, cmd.tcpm_keylen); } return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);}static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, struct in6_addr *saddr, struct in6_addr *daddr, struct tcphdr *th, int protocol, int tcplen){ struct scatterlist sg[4]; __u16 data_len; int block = 0; __sum16 cksum; struct tcp_md5sig_pool *hp; struct tcp6_pseudohdr *bp; struct hash_desc *desc; int err; unsigned int nbytes = 0; hp = tcp_get_md5sig_pool(); if (!hp) { printk(KERN_WARNING "%s(): hash pool not found...\n", __FUNCTION__); goto clear_hash_noput; } bp = &hp->md5_blk.ip6; desc = &hp->md5_desc; /* 1. TCP pseudo-header (RFC2460) */ ipv6_addr_copy(&bp->saddr, saddr); ipv6_addr_copy(&bp->daddr, daddr); bp->len = htonl(tcplen); bp->protocol = htonl(protocol); sg_init_table(sg, 4); sg_set_buf(&sg[block++], bp, sizeof(*bp)); nbytes += sizeof(*bp); /* 2. TCP header, excluding options */ cksum = th->check; th->check = 0; sg_set_buf(&sg[block++], th, sizeof(*th)); nbytes += sizeof(*th); /* 3. TCP segment data (if any) */ data_len = tcplen - (th->doff << 2); if (data_len > 0) { u8 *data = (u8 *)th + (th->doff << 2); sg_set_buf(&sg[block++], data, data_len); nbytes += data_len; } /* 4. shared key */ sg_set_buf(&sg[block++], key->key, key->keylen); nbytes += key->keylen; sg_mark_end(&sg[block - 1]); /* Now store the hash into the packet */ err = crypto_hash_init(desc); if (err) { printk(KERN_WARNING "%s(): hash_init failed\n", __FUNCTION__); goto clear_hash; } err = crypto_hash_update(desc, sg, nbytes); if (err) { printk(KERN_WARNING "%s(): hash_update failed\n", __FUNCTION__); goto clear_hash; } err = crypto_hash_final(desc, md5_hash); if (err) { printk(KERN_WARNING "%s(): hash_final failed\n", __FUNCTION__); goto clear_hash; } /* Reset header, and free up the crypto */ tcp_put_md5sig_pool(); th->check = cksum;out: return 0;clear_hash: tcp_put_md5sig_pool();clear_hash_noput: memset(md5_hash, 0, 16); goto out;}static int tcp_v6_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, struct sock *sk, struct dst_entry *dst, struct request_sock *req, struct tcphdr *th, int protocol, int tcplen){ struct in6_addr *saddr, *daddr; if (sk) { saddr = &inet6_sk(sk)->saddr; daddr = &inet6_sk(sk)->daddr; } else { saddr = &inet6_rsk(req)->loc_addr; daddr = &inet6_rsk(req)->rmt_addr; } return tcp_v6_do_calc_md5_hash(md5_hash, key, saddr, daddr, th, protocol, tcplen);}static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb){ __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; struct ipv6hdr *ip6h = ipv6_hdr(skb); struct tcphdr *th = tcp_hdr(skb); int length = (th->doff << 2) - sizeof (*th); int genhash; u8 *ptr; u8 newhash[16]; hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr); /* If the TCP option is too short, we can short cut */ if (length < TCPOLEN_MD5SIG) return hash_expected ? 1 : 0; /* parse options */ ptr = (u8*)(th + 1); while (length > 0) { int opcode = *ptr++; int opsize; switch(opcode) { case TCPOPT_EOL: goto done_opts; case TCPOPT_NOP: length--; continue; default: opsize = *ptr++; if (opsize < 2 || opsize > length) goto done_opts; if (opcode == TCPOPT_MD5SIG) { hash_location = ptr; goto done_opts; } } ptr += opsize - 2; length -= opsize; }done_opts: /* do we have a hash as expected? */ if (!hash_expected) { if (!hash_location) return 0; if (net_ratelimit()) { printk(KERN_INFO "MD5 Hash NOT expected but found " "(" NIP6_FMT ", %u)->" "(" NIP6_FMT ", %u)\n", NIP6(ip6h->saddr), ntohs(th->source), NIP6(ip6h->daddr), ntohs(th->dest)); } return 1; } if (!hash_location) { if (net_ratelimit()) { printk(KERN_INFO "MD5 Hash expected but NOT found " "(" NIP6_FMT ", %u)->" "(" NIP6_FMT ", %u)\n", NIP6(ip6h->saddr), ntohs(th->source), NIP6(ip6h->daddr), ntohs(th->dest)); } return 1; } /* check the signature */ genhash = tcp_v6_do_calc_md5_hash(newhash, hash_expected, &ip6h->saddr, &ip6h->daddr, th, sk->sk_protocol, skb->len); if (genhash || memcmp(hash_location, newhash, 16) != 0) { if (net_ratelimit()) { printk(KERN_INFO "MD5 Hash %s for " "(" NIP6_FMT ", %u)->" "(" NIP6_FMT ", %u)\n", genhash ? "failed" : "mismatch", NIP6(ip6h->saddr), ntohs(th->source), NIP6(ip6h->daddr), ntohs(th->dest)); } return 1; } return 0;}#endifstatic struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .family = AF_INET6, .obj_size = sizeof(struct tcp6_request_sock), .rtx_syn_ack = tcp_v6_send_synack, .send_ack = tcp_v6_reqsk_send_ack, .destructor = tcp_v6_reqsk_destructor, .send_reset = tcp_v6_send_reset};#ifdef CONFIG_TCP_MD5SIGstatic struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .md5_lookup = tcp_v6_reqsk_md5_lookup,};#endifstatic struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_obj_size = sizeof(struct tcp6_timewait_sock), .twsk_unique = tcp_twsk_unique, .twsk_destructor= tcp_twsk_destructor,};static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb){ struct ipv6_pinfo *np = inet6_sk(sk); struct tcphdr *th = tcp_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); } else { th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, csum_partial((char *)th, th->doff<<2, skb->csum)); }}static int tcp_v6_gso_send_check(struct sk_buff *skb){ struct ipv6hdr *ipv6h; struct tcphdr *th; if (!pskb_may_pull(skb, sizeof(*th))) return -EINVAL; ipv6h = ipv6_hdr(skb); th = tcp_hdr(skb); th->check = 0; th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, IPPROTO_TCP, 0); skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; return 0;}static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb){ struct tcphdr *th = tcp_hdr(skb), *t1; struct sk_buff *buff; struct flowi fl; int tot_len = sizeof(*th);#ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key;#endif if (th->rst) return; if (!ipv6_unicast_destination(skb)) return;#ifdef CONFIG_TCP_MD5SIG if (sk) key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr); else key = NULL; if (key) tot_len += TCPOLEN_MD5SIG_ALIGNED;#endif /* * We need to grab some memory, and put together an RST, * and then put it into the queue to be sent. */ buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, GFP_ATOMIC); if (buff == NULL) return; skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len); t1 = (struct tcphdr *) skb_push(buff, tot_len); /* Swap the send and the receive. */ memset(t1, 0, sizeof(*t1)); t1->dest = th->source; t1->source = th->dest; t1->doff = tot_len / 4; t1->rst = 1; if(th->ack) { t1->seq = th->ack_seq; } else { t1->ack = 1; t1->ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff<<2)); }#ifdef CONFIG_TCP_MD5SIG if (key) { __be32 *opt = (__be32*)(t1 + 1); opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr, t1, IPPROTO_TCP, tot_len); }#endif buff->csum = csum_partial((char *)t1, sizeof(*t1), 0); memset(&fl, 0, sizeof(fl)); ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr); ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr); t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst, sizeof(*t1), IPPROTO_TCP, buff->csum); fl.proto = IPPROTO_TCP; fl.oif = inet6_iif(skb); fl.fl_ip_dport = t1->dest; fl.fl_ip_sport = t1->source; security_skb_classify_flow(skb, &fl); /* sk = NULL, but it is safe for now. RST socket required. */ if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) { if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) { ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(TCP_MIB_OUTRSTS); return; } } kfree_skb(buff);}static void tcp_v6_send_ack(struct tcp_timewait_sock *tw, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts){ struct tcphdr *th = tcp_hdr(skb), *t1; struct sk_buff *buff; struct flowi fl; int tot_len = sizeof(struct tcphdr); __be32 *topt;#ifdef CONFIG_TCP_MD5SIG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -