aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoopa Prabhu <roopa@cumulusnetworks.com>2018-02-28 22:43:22 -0500
committerDavid S. Miller <davem@davemloft.net>2018-02-28 22:44:44 -0500
commit5e5d6fed374155ba1a7a5ca5f12fbec2285d06a2 (patch)
tree92e19ce9d5e58ee33bbface8a6b5f4001c28a06c
parente37b1e978bec5334dc379d8c2423d063af207430 (diff)
ipv6: route: dissect flow in input path if fib rules need it
Dissect flow in fwd path if fib rules require it. Controlled by a flag to avoid penatly for the common case. Flag is set when fib rules with sport, dport and proto match that require flow dissect are installed. Also passes the dissected hash keys to the multipath hash function when applicable to avoid dissecting the flow again. icmp packets will continue to use inner header for hash calculations. Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> Acked-by: Paolo Abeni <pabeni@redhat.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip6_fib.h25
-rw-r--r--include/net/ip6_route.h4
-rw-r--r--include/net/netns/ipv6.h3
-rw-r--r--net/ipv6/fib6_rules.c16
-rw-r--r--net/ipv6/icmp.c2
-rw-r--r--net/ipv6/route.c34
6 files changed, 72 insertions, 12 deletions
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 34ec321d6a03..8d906a35b534 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -415,6 +415,24 @@ void fib6_rules_cleanup(void);
415bool fib6_rule_default(const struct fib_rule *rule); 415bool fib6_rule_default(const struct fib_rule *rule);
416int fib6_rules_dump(struct net *net, struct notifier_block *nb); 416int fib6_rules_dump(struct net *net, struct notifier_block *nb);
417unsigned int fib6_rules_seq_read(struct net *net); 417unsigned int fib6_rules_seq_read(struct net *net);
418
419static inline bool fib6_rules_early_flow_dissect(struct net *net,
420 struct sk_buff *skb,
421 struct flowi6 *fl6,
422 struct flow_keys *flkeys)
423{
424 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
425
426 if (!net->ipv6.fib6_rules_require_fldissect)
427 return false;
428
429 skb_flow_dissect_flow_keys(skb, flkeys, flag);
430 fl6->fl6_sport = flkeys->ports.src;
431 fl6->fl6_dport = flkeys->ports.dst;
432 fl6->flowi6_proto = flkeys->basic.ip_proto;
433
434 return true;
435}
418#else 436#else
419static inline int fib6_rules_init(void) 437static inline int fib6_rules_init(void)
420{ 438{
@@ -436,5 +454,12 @@ static inline unsigned int fib6_rules_seq_read(struct net *net)
436{ 454{
437 return 0; 455 return 0;
438} 456}
457static inline bool fib6_rules_early_flow_dissect(struct net *net,
458 struct sk_buff *skb,
459 struct flowi6 *fl6,
460 struct flow_keys *flkeys)
461{
462 return false;
463}
439#endif 464#endif
440#endif 465#endif
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 27d23a65f3cd..da2bde5fda8f 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -127,7 +127,8 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
127 127
128struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 128struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
129 const struct in6_addr *saddr, int oif, int flags); 129 const struct in6_addr *saddr, int oif, int flags);
130u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb); 130u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
131 struct flow_keys *hkeys);
131 132
132struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6); 133struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
133 134
@@ -266,4 +267,5 @@ static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
266 ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) && 267 ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
267 !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate); 268 !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
268} 269}
270
269#endif 271#endif
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 987cc4569cb8..2b9194229a56 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -71,7 +71,8 @@ struct netns_ipv6 {
71 unsigned int ip6_rt_gc_expire; 71 unsigned int ip6_rt_gc_expire;
72 unsigned long ip6_rt_last_gc; 72 unsigned long ip6_rt_last_gc;
73#ifdef CONFIG_IPV6_MULTIPLE_TABLES 73#ifdef CONFIG_IPV6_MULTIPLE_TABLES
74 bool fib6_has_custom_rules; 74 unsigned int fib6_rules_require_fldissect;
75 bool fib6_has_custom_rules;
75 struct rt6_info *ip6_prohibit_entry; 76 struct rt6_info *ip6_prohibit_entry;
76 struct rt6_info *ip6_blk_hole_entry; 77 struct rt6_info *ip6_blk_hole_entry;
77 struct fib6_table *fib6_local_tbl; 78 struct fib6_table *fib6_local_tbl;
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index bcd1f22ac7b1..04e5f523e50f 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -269,12 +269,26 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
269 rule6->dst.plen = frh->dst_len; 269 rule6->dst.plen = frh->dst_len;
270 rule6->tclass = frh->tos; 270 rule6->tclass = frh->tos;
271 271
272 if (fib_rule_requires_fldissect(rule))
273 net->ipv6.fib6_rules_require_fldissect++;
274
272 net->ipv6.fib6_has_custom_rules = true; 275 net->ipv6.fib6_has_custom_rules = true;
273 err = 0; 276 err = 0;
274errout: 277errout:
275 return err; 278 return err;
276} 279}
277 280
281static int fib6_rule_delete(struct fib_rule *rule)
282{
283 struct net *net = rule->fr_net;
284
285 if (net->ipv6.fib6_rules_require_fldissect &&
286 fib_rule_requires_fldissect(rule))
287 net->ipv6.fib6_rules_require_fldissect--;
288
289 return 0;
290}
291
278static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 292static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
279 struct nlattr **tb) 293 struct nlattr **tb)
280{ 294{
@@ -334,6 +348,7 @@ static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
334 .match = fib6_rule_match, 348 .match = fib6_rule_match,
335 .suppress = fib6_rule_suppress, 349 .suppress = fib6_rule_suppress,
336 .configure = fib6_rule_configure, 350 .configure = fib6_rule_configure,
351 .delete = fib6_rule_delete,
337 .compare = fib6_rule_compare, 352 .compare = fib6_rule_compare,
338 .fill = fib6_rule_fill, 353 .fill = fib6_rule_fill,
339 .nlmsg_payload = fib6_rule_nlmsg_payload, 354 .nlmsg_payload = fib6_rule_nlmsg_payload,
@@ -361,6 +376,7 @@ static int __net_init fib6_rules_net_init(struct net *net)
361 goto out_fib6_rules_ops; 376 goto out_fib6_rules_ops;
362 377
363 net->ipv6.fib6_rules_ops = ops; 378 net->ipv6.fib6_rules_ops = ops;
379 net->ipv6.fib6_rules_require_fldissect = 0;
364out: 380out:
365 return err; 381 return err;
366 382
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 4fa4f1b150a4..b0778d323b6e 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -522,7 +522,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
522 fl6.fl6_icmp_type = type; 522 fl6.fl6_icmp_type = type;
523 fl6.fl6_icmp_code = code; 523 fl6.fl6_icmp_code = code;
524 fl6.flowi6_uid = sock_net_uid(net, NULL); 524 fl6.flowi6_uid = sock_net_uid(net, NULL);
525 fl6.mp_hash = rt6_multipath_hash(&fl6, skb); 525 fl6.mp_hash = rt6_multipath_hash(&fl6, skb, NULL);
526 security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); 526 security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
527 527
528 sk = icmpv6_xmit_lock(net); 528 sk = icmpv6_xmit_lock(net);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index aa709b644945..e2bb40824c85 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -460,7 +460,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
460 * case it will always be non-zero. Otherwise now is the time to do it. 460 * case it will always be non-zero. Otherwise now is the time to do it.
461 */ 461 */
462 if (!fl6->mp_hash) 462 if (!fl6->mp_hash)
463 fl6->mp_hash = rt6_multipath_hash(fl6, NULL); 463 fl6->mp_hash = rt6_multipath_hash(fl6, NULL, NULL);
464 464
465 if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound)) 465 if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
466 return match; 466 return match;
@@ -1786,10 +1786,12 @@ struct dst_entry *ip6_route_input_lookup(struct net *net,
1786EXPORT_SYMBOL_GPL(ip6_route_input_lookup); 1786EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
1787 1787
1788static void ip6_multipath_l3_keys(const struct sk_buff *skb, 1788static void ip6_multipath_l3_keys(const struct sk_buff *skb,
1789 struct flow_keys *keys) 1789 struct flow_keys *keys,
1790 struct flow_keys *flkeys)
1790{ 1791{
1791 const struct ipv6hdr *outer_iph = ipv6_hdr(skb); 1792 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1792 const struct ipv6hdr *key_iph = outer_iph; 1793 const struct ipv6hdr *key_iph = outer_iph;
1794 struct flow_keys *_flkeys = flkeys;
1793 const struct ipv6hdr *inner_iph; 1795 const struct ipv6hdr *inner_iph;
1794 const struct icmp6hdr *icmph; 1796 const struct icmp6hdr *icmph;
1795 struct ipv6hdr _inner_iph; 1797 struct ipv6hdr _inner_iph;
@@ -1811,22 +1813,31 @@ static void ip6_multipath_l3_keys(const struct sk_buff *skb,
1811 goto out; 1813 goto out;
1812 1814
1813 key_iph = inner_iph; 1815 key_iph = inner_iph;
1816 _flkeys = NULL;
1814out: 1817out:
1815 memset(keys, 0, sizeof(*keys)); 1818 memset(keys, 0, sizeof(*keys));
1816 keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1819 keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
1817 keys->addrs.v6addrs.src = key_iph->saddr; 1820 if (_flkeys) {
1818 keys->addrs.v6addrs.dst = key_iph->daddr; 1821 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
1819 keys->tags.flow_label = ip6_flowinfo(key_iph); 1822 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
1820 keys->basic.ip_proto = key_iph->nexthdr; 1823 keys->tags.flow_label = _flkeys->tags.flow_label;
1824 keys->basic.ip_proto = _flkeys->basic.ip_proto;
1825 } else {
1826 keys->addrs.v6addrs.src = key_iph->saddr;
1827 keys->addrs.v6addrs.dst = key_iph->daddr;
1828 keys->tags.flow_label = ip6_flowinfo(key_iph);
1829 keys->basic.ip_proto = key_iph->nexthdr;
1830 }
1821} 1831}
1822 1832
1823/* if skb is set it will be used and fl6 can be NULL */ 1833/* if skb is set it will be used and fl6 can be NULL */
1824u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb) 1834u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
1835 struct flow_keys *flkeys)
1825{ 1836{
1826 struct flow_keys hash_keys; 1837 struct flow_keys hash_keys;
1827 1838
1828 if (skb) { 1839 if (skb) {
1829 ip6_multipath_l3_keys(skb, &hash_keys); 1840 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
1830 return flow_hash_from_keys(&hash_keys) >> 1; 1841 return flow_hash_from_keys(&hash_keys) >> 1;
1831 } 1842 }
1832 1843
@@ -1847,12 +1858,17 @@ void ip6_route_input(struct sk_buff *skb)
1847 .flowi6_mark = skb->mark, 1858 .flowi6_mark = skb->mark,
1848 .flowi6_proto = iph->nexthdr, 1859 .flowi6_proto = iph->nexthdr,
1849 }; 1860 };
1861 struct flow_keys *flkeys = NULL, _flkeys;
1850 1862
1851 tun_info = skb_tunnel_info(skb); 1863 tun_info = skb_tunnel_info(skb);
1852 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 1864 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1853 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 1865 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
1866
1867 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
1868 flkeys = &_flkeys;
1869
1854 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) 1870 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
1855 fl6.mp_hash = rt6_multipath_hash(&fl6, skb); 1871 fl6.mp_hash = rt6_multipath_hash(&fl6, skb, flkeys);
1856 skb_dst_drop(skb); 1872 skb_dst_drop(skb);
1857 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1873 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1858} 1874}