Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: Allow request socks to contain IPv6 options.

If set, these will take precedence over the parent's options during
both sending and child creation. If they're not set, the parent's
options (if any) will be used.

This is to allow the security_inet_conn_request() hook to modify the
IPv6 options in just the same way that it already may do for IPv4.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Huw Davies and committed by
Paul Moore
56ac42bc 1f440c99

+27 -7
+6 -1
include/net/inet_sock.h
··· 97 97 u32 ir_mark; 98 98 union { 99 99 struct ip_options_rcu *opt; 100 - struct sk_buff *pktopts; 100 + #if IS_ENABLED(CONFIG_IPV6) 101 + struct { 102 + struct ipv6_txoptions *ipv6_opt; 103 + struct sk_buff *pktopts; 104 + }; 105 + #endif 101 106 }; 102 107 }; 103 108
+9 -3
net/dccp/ipv6.c
··· 216 216 skb = dccp_make_response(sk, dst, req); 217 217 if (skb != NULL) { 218 218 struct dccp_hdr *dh = dccp_hdr(skb); 219 + struct ipv6_txoptions *opt; 219 220 220 221 dh->dccph_checksum = dccp_v6_csum_finish(skb, 221 222 &ireq->ir_v6_loc_addr, 222 223 &ireq->ir_v6_rmt_addr); 223 224 fl6.daddr = ireq->ir_v6_rmt_addr; 224 225 rcu_read_lock(); 225 - err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), 226 - np->tclass); 226 + opt = ireq->ipv6_opt; 227 + if (!opt) 228 + opt = rcu_dereference(np->opt); 229 + err = ip6_xmit(sk, skb, &fl6, opt, np->tclass); 227 230 rcu_read_unlock(); 228 231 err = net_xmit_eval(err); 229 232 } ··· 239 236 static void dccp_v6_reqsk_destructor(struct request_sock *req) 240 237 { 241 238 dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); 239 + kfree(inet_rsk(req)->ipv6_opt); 242 240 kfree_skb(inet_rsk(req)->pktopts); 243 241 } 244 242 ··· 498 494 * Yes, keeping reference count would be much more clever, but we make 499 495 * one more one thing there: reattach optmem to newsk. 500 496 */ 501 - opt = rcu_dereference(np->opt); 497 + opt = ireq->ipv6_opt; 498 + if (!opt) 499 + opt = rcu_dereference(np->opt); 502 500 if (opt) { 503 501 opt = ipv6_dup_options(newsk, opt); 504 502 RCU_INIT_POINTER(newnp->opt, opt);
+3
net/ipv4/tcp_input.c
··· 6146 6146 6147 6147 kmemcheck_annotate_bitfield(ireq, flags); 6148 6148 ireq->opt = NULL; 6149 + #if IS_ENABLED(CONFIG_IPV6) 6150 + ireq->pktopts = NULL; 6151 + #endif 6149 6152 atomic64_set(&ireq->ir_cookie, 0); 6150 6153 ireq->ireq_state = TCP_NEW_SYN_RECV; 6151 6154 write_pnet(&ireq->ireq_net, sock_net(sk_listener));
+9 -3
net/ipv6/tcp_ipv6.c
··· 443 443 { 444 444 struct inet_request_sock *ireq = inet_rsk(req); 445 445 struct ipv6_pinfo *np = inet6_sk(sk); 446 + struct ipv6_txoptions *opt; 446 447 struct flowi6 *fl6 = &fl->u.ip6; 447 448 struct sk_buff *skb; 448 449 int err = -ENOMEM; ··· 464 463 fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); 465 464 466 465 rcu_read_lock(); 467 - err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), 468 - np->tclass); 466 + opt = ireq->ipv6_opt; 467 + if (!opt) 468 + opt = rcu_dereference(np->opt); 469 + err = ip6_xmit(sk, skb, fl6, opt, np->tclass); 469 470 rcu_read_unlock(); 470 471 err = net_xmit_eval(err); 471 472 } ··· 479 476 480 477 static void tcp_v6_reqsk_destructor(struct request_sock *req) 481 478 { 479 + kfree(inet_rsk(req)->ipv6_opt); 482 480 kfree_skb(inet_rsk(req)->pktopts); 483 481 } 484 482 ··· 1111 1107 but we make one more one thing there: reattach optmem 1112 1108 to newsk. 1113 1109 */ 1114 - opt = rcu_dereference(np->opt); 1110 + opt = ireq->ipv6_opt; 1111 + if (!opt) 1112 + opt = rcu_dereference(np->opt); 1115 1113 if (opt) { 1116 1114 opt = ipv6_dup_options(newsk, opt); 1117 1115 RCU_INIT_POINTER(newnp->opt, opt);