9b0100d6bce604f56932fbcc4bbaad9f7bef2b12
[librecmc/librecmc.git] /
1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Wed, 24 Mar 2021 02:30:32 +0100
3 Subject: [PATCH] net: resolve forwarding path from virtual netdevice and
4  HW destination address
5
6 This patch adds dev_fill_forward_path() which resolves the path to reach
7 the real netdevice from the IP forwarding side. This function takes as
8 input the netdevice and the destination hardware address and it walks
9 down the devices calling .ndo_fill_forward_path() for each device until
10 the real device is found.
11
12 For instance, assuming the following topology:
13
14                IP forwarding
15               /             \
16            br0              eth0
17            / \
18        eth1  eth2
19         .
20         .
21         .
22        ethX
23  ab:cd:ef:ab:cd:ef
24
25 where eth1 and eth2 are bridge ports and eth0 provides WAN connectivity.
26 ethX is the interface in another box which is connected to the eth1
27 bridge port.
28
29 For packets going through IP forwarding to br0 whose destination MAC
30 address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
31 following path:
32
33         br0 -> eth1
34
35 .ndo_fill_forward_path for br0 looks up at the FDB for the bridge port
36 from the destination MAC address to get the bridge port eth1.
37
38 This information allows to create a fast path that bypasses the classic
39 bridge and IP forwarding paths, so packets go directly from the bridge
40 port eth1 to eth0 (wan interface) and vice versa.
41
42              fast path
43       .------------------------.
44      /                          \
45     |           IP forwarding   |
46     |          /             \  \/
47     |       br0               eth0
48     .       / \
49      -> eth1  eth2
50         .
51         .
52         .
53        ethX
54  ab:cd:ef:ab:cd:ef
55
56 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
57 ---
58
59 --- a/include/linux/netdevice.h
60 +++ b/include/linux/netdevice.h
61 @@ -841,6 +841,27 @@ typedef u16 (*select_queue_fallback_t)(s
62                                        struct sk_buff *skb,
63                                        struct net_device *sb_dev);
64  
65 +enum net_device_path_type {
66 +       DEV_PATH_ETHERNET = 0,
67 +};
68 +
69 +struct net_device_path {
70 +       enum net_device_path_type       type;
71 +       const struct net_device         *dev;
72 +};
73 +
74 +#define NET_DEVICE_PATH_STACK_MAX      5
75 +
76 +struct net_device_path_stack {
77 +       int                     num_paths;
78 +       struct net_device_path  path[NET_DEVICE_PATH_STACK_MAX];
79 +};
80 +
81 +struct net_device_path_ctx {
82 +       const struct net_device *dev;
83 +       const u8                *daddr;
84 +};
85 +
86  enum tc_setup_type {
87         TC_SETUP_QDISC_MQPRIO,
88         TC_SETUP_CLSU32,
89 @@ -1287,6 +1308,8 @@ struct netdev_net_notifier {
90   * struct net_device *(*ndo_get_peer_dev)(struct net_device *dev);
91   *     If a device is paired with a peer device, return the peer instance.
92   *     The caller must be under RCU read context.
93 + * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path);
94 + *     Get the forwarding path to reach the real device from the HW destination address
95   */
96  struct net_device_ops {
97         int                     (*ndo_init)(struct net_device *dev);
98 @@ -1495,6 +1518,8 @@ struct net_device_ops {
99         int                     (*ndo_tunnel_ctl)(struct net_device *dev,
100                                                   struct ip_tunnel_parm *p, int cmd);
101         struct net_device *     (*ndo_get_peer_dev)(struct net_device *dev);
102 +       int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
103 +                                                         struct net_device_path *path);
104  };
105  
106  /**
107 @@ -2842,6 +2867,8 @@ void dev_remove_offload(struct packet_of
108  
109  int dev_get_iflink(const struct net_device *dev);
110  int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
111 +int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
112 +                         struct net_device_path_stack *stack);
113  struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
114                                       unsigned short mask);
115  struct net_device *dev_get_by_name(struct net *net, const char *name);
116 --- a/net/core/dev.c
117 +++ b/net/core/dev.c
118 @@ -847,6 +847,52 @@ int dev_fill_metadata_dst(struct net_dev
119  }
120  EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
121  
122 +static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
123 +{
124 +       int k = stack->num_paths++;
125 +
126 +       if (WARN_ON_ONCE(k >= NET_DEVICE_PATH_STACK_MAX))
127 +               return NULL;
128 +
129 +       return &stack->path[k];
130 +}
131 +
132 +int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
133 +                         struct net_device_path_stack *stack)
134 +{
135 +       const struct net_device *last_dev;
136 +       struct net_device_path_ctx ctx = {
137 +               .dev    = dev,
138 +               .daddr  = daddr,
139 +       };
140 +       struct net_device_path *path;
141 +       int ret = 0;
142 +
143 +       stack->num_paths = 0;
144 +       while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
145 +               last_dev = ctx.dev;
146 +               path = dev_fwd_path(stack);
147 +               if (!path)
148 +                       return -1;
149 +
150 +               memset(path, 0, sizeof(struct net_device_path));
151 +               ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
152 +               if (ret < 0)
153 +                       return -1;
154 +
155 +               if (WARN_ON_ONCE(last_dev == ctx.dev))
156 +                       return -1;
157 +       }
158 +       path = dev_fwd_path(stack);
159 +       if (!path)
160 +               return -1;
161 +       path->type = DEV_PATH_ETHERNET;
162 +       path->dev = ctx.dev;
163 +
164 +       return ret;
165 +}
166 +EXPORT_SYMBOL_GPL(dev_fill_forward_path);
167 +
168  /**
169   *     __dev_get_by_name       - find a device by its name
170   *     @net: the applicable net namespace