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

tcp: md5: Allow MD5SIG_FLAG_IFINDEX with ifindex=0

Multiple VRFs are generally meant to be "separate" but right now md5
keys for the default VRF also affect connections inside VRFs if the IP
addresses happen to overlap.

So far the combination of TCP_MD5SIG_FLAG_IFINDEX with tcpm_ifindex == 0
was an error, accept this to mean "key only applies to default VRF".
This is what applications using VRFs for traffic separation want.

Signed-off-by: Leonard Crestez <cdleonard@gmail.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Leonard Crestez and committed by
David S. Miller
a76c2315 86f1e3a8

+28 -18
+3 -2
include/net/tcp.h
··· 1576 1576 u8 keylen; 1577 1577 u8 family; /* AF_INET or AF_INET6 */ 1578 1578 u8 prefixlen; 1579 + u8 flags; 1579 1580 union tcp_md5_addr addr; 1580 1581 int l3index; /* set if key added with L3 scope */ 1581 1582 u8 key[TCP_MD5SIG_MAXKEYLEN]; ··· 1622 1621 int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, 1623 1622 const struct sock *sk, const struct sk_buff *skb); 1624 1623 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, 1625 - int family, u8 prefixlen, int l3index, 1624 + int family, u8 prefixlen, int l3index, u8 flags, 1626 1625 const u8 *newkey, u8 newkeylen, gfp_t gfp); 1627 1626 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, 1628 - int family, u8 prefixlen, int l3index); 1627 + int family, u8 prefixlen, int l3index, u8 flags); 1629 1628 struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, 1630 1629 const struct sock *addr_sk); 1631 1630
+16 -10
net/ipv4/tcp_ipv4.c
··· 1073 1073 lockdep_sock_is_held(sk)) { 1074 1074 if (key->family != family) 1075 1075 continue; 1076 - if (key->l3index && key->l3index != l3index) 1076 + if (key->flags & TCP_MD5SIG_FLAG_IFINDEX && key->l3index != l3index) 1077 1077 continue; 1078 1078 if (family == AF_INET) { 1079 1079 mask = inet_make_mask(key->prefixlen); ··· 1098 1098 static struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk, 1099 1099 const union tcp_md5_addr *addr, 1100 1100 int family, u8 prefixlen, 1101 - int l3index) 1101 + int l3index, u8 flags) 1102 1102 { 1103 1103 const struct tcp_sock *tp = tcp_sk(sk); 1104 1104 struct tcp_md5sig_key *key; ··· 1117 1117 hlist_for_each_entry_rcu(key, &md5sig->head, node, 1118 1118 lockdep_sock_is_held(sk)) { 1119 1119 if (key->family != family) 1120 + continue; 1121 + if ((key->flags & TCP_MD5SIG_FLAG_IFINDEX) != (flags & TCP_MD5SIG_FLAG_IFINDEX)) 1120 1122 continue; 1121 1123 if (key->l3index != l3index) 1122 1124 continue; ··· 1144 1142 1145 1143 /* This can be called on a newly created socket, from other files */ 1146 1144 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, 1147 - int family, u8 prefixlen, int l3index, 1145 + int family, u8 prefixlen, int l3index, u8 flags, 1148 1146 const u8 *newkey, u8 newkeylen, gfp_t gfp) 1149 1147 { 1150 1148 /* Add Key to the list */ ··· 1152 1150 struct tcp_sock *tp = tcp_sk(sk); 1153 1151 struct tcp_md5sig_info *md5sig; 1154 1152 1155 - key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index); 1153 + key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index, flags); 1156 1154 if (key) { 1157 1155 /* Pre-existing entry - just update that one. 1158 1156 * Note that the key might be used concurrently. ··· 1197 1195 key->family = family; 1198 1196 key->prefixlen = prefixlen; 1199 1197 key->l3index = l3index; 1198 + key->flags = flags; 1200 1199 memcpy(&key->addr, addr, 1201 1200 (family == AF_INET6) ? sizeof(struct in6_addr) : 1202 1201 sizeof(struct in_addr)); ··· 1207 1204 EXPORT_SYMBOL(tcp_md5_do_add); 1208 1205 1209 1206 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, 1210 - u8 prefixlen, int l3index) 1207 + u8 prefixlen, int l3index, u8 flags) 1211 1208 { 1212 1209 struct tcp_md5sig_key *key; 1213 1210 1214 - key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index); 1211 + key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index, flags); 1215 1212 if (!key) 1216 1213 return -ENOENT; 1217 1214 hlist_del_rcu(&key->node); ··· 1245 1242 const union tcp_md5_addr *addr; 1246 1243 u8 prefixlen = 32; 1247 1244 int l3index = 0; 1245 + u8 flags; 1248 1246 1249 1247 if (optlen < sizeof(cmd)) 1250 1248 return -EINVAL; ··· 1256 1252 if (sin->sin_family != AF_INET) 1257 1253 return -EINVAL; 1258 1254 1255 + flags = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; 1256 + 1259 1257 if (optname == TCP_MD5SIG_EXT && 1260 1258 cmd.tcpm_flags & TCP_MD5SIG_FLAG_PREFIX) { 1261 1259 prefixlen = cmd.tcpm_prefixlen; ··· 1265 1259 return -EINVAL; 1266 1260 } 1267 1261 1268 - if (optname == TCP_MD5SIG_EXT && 1262 + if (optname == TCP_MD5SIG_EXT && cmd.tcpm_ifindex && 1269 1263 cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { 1270 1264 struct net_device *dev; 1271 1265 ··· 1286 1280 addr = (union tcp_md5_addr *)&sin->sin_addr.s_addr; 1287 1281 1288 1282 if (!cmd.tcpm_keylen) 1289 - return tcp_md5_do_del(sk, addr, AF_INET, prefixlen, l3index); 1283 + return tcp_md5_do_del(sk, addr, AF_INET, prefixlen, l3index, flags); 1290 1284 1291 1285 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) 1292 1286 return -EINVAL; 1293 1287 1294 - return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, 1288 + return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, 1295 1289 cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 1296 1290 } 1297 1291 ··· 1615 1609 * memory, then we end up not copying the key 1616 1610 * across. Shucks. 1617 1611 */ 1618 - tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, 1612 + tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags, 1619 1613 key->key, key->keylen, GFP_ATOMIC); 1620 1614 sk_nocaps_add(newsk, NETIF_F_GSO_MASK); 1621 1615 }
+9 -6
net/ipv6/tcp_ipv6.c
··· 599 599 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; 600 600 int l3index = 0; 601 601 u8 prefixlen; 602 + u8 flags; 602 603 603 604 if (optlen < sizeof(cmd)) 604 605 return -EINVAL; ··· 609 608 610 609 if (sin6->sin6_family != AF_INET6) 611 610 return -EINVAL; 611 + 612 + flags = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; 612 613 613 614 if (optname == TCP_MD5SIG_EXT && 614 615 cmd.tcpm_flags & TCP_MD5SIG_FLAG_PREFIX) { ··· 622 619 prefixlen = ipv6_addr_v4mapped(&sin6->sin6_addr) ? 32 : 128; 623 620 } 624 621 625 - if (optname == TCP_MD5SIG_EXT && 622 + if (optname == TCP_MD5SIG_EXT && cmd.tcpm_ifindex && 626 623 cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { 627 624 struct net_device *dev; 628 625 ··· 643 640 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 644 641 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], 645 642 AF_INET, prefixlen, 646 - l3index); 643 + l3index, flags); 647 644 return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, 648 - AF_INET6, prefixlen, l3index); 645 + AF_INET6, prefixlen, l3index, flags); 649 646 } 650 647 651 648 if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) ··· 653 650 654 651 if (ipv6_addr_v4mapped(&sin6->sin6_addr)) 655 652 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], 656 - AF_INET, prefixlen, l3index, 653 + AF_INET, prefixlen, l3index, flags, 657 654 cmd.tcpm_key, cmd.tcpm_keylen, 658 655 GFP_KERNEL); 659 656 660 657 return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, 661 - AF_INET6, prefixlen, l3index, 658 + AF_INET6, prefixlen, l3index, flags, 662 659 cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 663 660 } 664 661 ··· 1407 1404 * across. Shucks. 1408 1405 */ 1409 1406 tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, 1410 - AF_INET6, 128, l3index, key->key, key->keylen, 1407 + AF_INET6, 128, l3index, key->flags, key->key, key->keylen, 1411 1408 sk_gfp_mask(sk, GFP_ATOMIC)); 1412 1409 } 1413 1410 #endif