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

Configure Feed

Select the types of activity you want to include in your feed.

netfilter: flowtable: consolidate xmit path

Use dev_queue_xmit() for the XMIT_NEIGH case. Store the interface index
of the real device behind the vlan/pppoe device, this introduces an
extra lookup for the real device in the xmit path because rt->dst.dev
provides the vlan/pppoe device.

XMIT_NEIGH now looks more similar to XMIT_DIRECT but the check for stale
dst and the neighbour lookup still remain in place which is convenient
to deal with network topology changes.

Note that nft_flow_route() needs to relax the check for _XMIT_NEIGH so
the existing basic xfrm offload (which only works in one direction) does
not break.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+57 -39
+1
include/net/netfilter/nf_flow_table.h
··· 141 141 union { 142 142 struct { 143 143 struct dst_entry *dst_cache; 144 + u32 ifidx; 144 145 u32 dst_cookie; 145 146 }; 146 147 struct {
+1
net/netfilter/nf_flow_table_core.c
··· 132 132 break; 133 133 case FLOW_OFFLOAD_XMIT_XFRM: 134 134 case FLOW_OFFLOAD_XMIT_NEIGH: 135 + flow_tuple->ifidx = route->tuple[dir].out.ifindex; 135 136 flow_tuple->dst_cache = dst; 136 137 flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple); 137 138 break;
+52 -35
net/netfilter/nf_flow_table_ip.c
··· 333 333 } 334 334 } 335 335 336 + struct nf_flow_xmit { 337 + const void *dest; 338 + const void *source; 339 + struct net_device *outdev; 340 + }; 341 + 336 342 static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, 337 - const struct flow_offload_tuple_rhash *tuplehash, 338 - unsigned short type) 343 + struct nf_flow_xmit *xmit) 339 344 { 340 - struct net_device *outdev; 341 - 342 - outdev = dev_get_by_index_rcu(net, tuplehash->tuple.out.ifidx); 343 - if (!outdev) 344 - return NF_DROP; 345 - 346 - skb->dev = outdev; 347 - dev_hard_header(skb, skb->dev, type, tuplehash->tuple.out.h_dest, 348 - tuplehash->tuple.out.h_source, skb->len); 345 + skb->dev = xmit->outdev; 346 + dev_hard_header(skb, skb->dev, ntohs(skb->protocol), 347 + xmit->dest, xmit->source, skb->len); 349 348 dev_queue_xmit(skb); 350 349 351 350 return NF_STOLEN; ··· 423 424 struct nf_flowtable_ctx ctx = { 424 425 .in = state->in, 425 426 }; 427 + struct nf_flow_xmit xmit = {}; 426 428 struct flow_offload *flow; 427 - struct net_device *outdev; 429 + struct neighbour *neigh; 428 430 struct rtable *rt; 429 - __be32 nexthop; 430 431 int ret; 431 432 432 433 tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb); ··· 453 454 switch (tuplehash->tuple.xmit_type) { 454 455 case FLOW_OFFLOAD_XMIT_NEIGH: 455 456 rt = dst_rtable(tuplehash->tuple.dst_cache); 456 - outdev = rt->dst.dev; 457 - skb->dev = outdev; 458 - nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr); 457 + xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx); 458 + if (!xmit.outdev) { 459 + flow_offload_teardown(flow); 460 + return NF_DROP; 461 + } 462 + neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr)); 463 + if (IS_ERR(neigh)) { 464 + flow_offload_teardown(flow); 465 + return NF_DROP; 466 + } 467 + xmit.dest = neigh->ha; 459 468 skb_dst_set_noref(skb, &rt->dst); 460 - neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb); 461 - ret = NF_STOLEN; 462 469 break; 463 470 case FLOW_OFFLOAD_XMIT_DIRECT: 464 - ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IP); 465 - if (ret == NF_DROP) 471 + xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx); 472 + if (!xmit.outdev) { 466 473 flow_offload_teardown(flow); 474 + return NF_DROP; 475 + } 476 + xmit.dest = tuplehash->tuple.out.h_dest; 477 + xmit.source = tuplehash->tuple.out.h_source; 467 478 break; 468 479 default: 469 480 WARN_ON_ONCE(1); 470 - ret = NF_DROP; 471 - break; 481 + return NF_DROP; 472 482 } 473 483 474 - return ret; 484 + return nf_flow_queue_xmit(state->net, skb, &xmit); 475 485 } 476 486 EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook); 477 487 ··· 727 719 struct nf_flowtable_ctx ctx = { 728 720 .in = state->in, 729 721 }; 730 - const struct in6_addr *nexthop; 722 + struct nf_flow_xmit xmit = {}; 731 723 struct flow_offload *flow; 732 - struct net_device *outdev; 724 + struct neighbour *neigh; 733 725 struct rt6_info *rt; 734 726 int ret; 735 727 ··· 757 749 switch (tuplehash->tuple.xmit_type) { 758 750 case FLOW_OFFLOAD_XMIT_NEIGH: 759 751 rt = dst_rt6_info(tuplehash->tuple.dst_cache); 760 - outdev = rt->dst.dev; 761 - skb->dev = outdev; 762 - nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); 752 + xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx); 753 + if (!xmit.outdev) { 754 + flow_offload_teardown(flow); 755 + return NF_DROP; 756 + } 757 + neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6)); 758 + if (IS_ERR(neigh)) { 759 + flow_offload_teardown(flow); 760 + return NF_DROP; 761 + } 762 + xmit.dest = neigh->ha; 763 763 skb_dst_set_noref(skb, &rt->dst); 764 - neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb); 765 - ret = NF_STOLEN; 766 764 break; 767 765 case FLOW_OFFLOAD_XMIT_DIRECT: 768 - ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IPV6); 769 - if (ret == NF_DROP) 766 + xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx); 767 + if (!xmit.outdev) { 770 768 flow_offload_teardown(flow); 769 + return NF_DROP; 770 + } 771 + xmit.dest = tuplehash->tuple.out.h_dest; 772 + xmit.source = tuplehash->tuple.out.h_source; 771 773 break; 772 774 default: 773 775 WARN_ON_ONCE(1); 774 - ret = NF_DROP; 775 - break; 776 + return NF_DROP; 776 777 } 777 778 778 - return ret; 779 + return nf_flow_queue_xmit(state->net, skb, &xmit); 779 780 } 780 781 EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
+3 -4
net/netfilter/nf_flow_table_path.c
··· 211 211 } 212 212 route->tuple[!dir].in.num_encaps = info.num_encaps; 213 213 route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; 214 + route->tuple[dir].out.ifindex = info.outdev->ifindex; 214 215 215 216 if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { 216 217 memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); 217 218 memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); 218 - route->tuple[dir].out.ifindex = info.outdev->ifindex; 219 219 route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; 220 220 route->tuple[dir].xmit_type = info.xmit_type; 221 221 } ··· 263 263 nft_default_forward_path(route, this_dst, dir); 264 264 nft_default_forward_path(route, other_dst, !dir); 265 265 266 - if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && 267 - route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { 266 + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) 268 267 nft_dev_forward_path(route, ct, dir, ft); 268 + if (route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) 269 269 nft_dev_forward_path(route, ct, !dir, ft); 270 - } 271 270 272 271 return 0; 273 272 }