1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef __NET_DST_METADATA_H |
3 | #define __NET_DST_METADATA_H 1 |
4 | |
5 | #include <linux/skbuff.h> |
6 | #include <net/ip_tunnels.h> |
7 | #include <net/macsec.h> |
8 | #include <net/dst.h> |
9 | |
10 | enum metadata_type { |
11 | METADATA_IP_TUNNEL, |
12 | METADATA_HW_PORT_MUX, |
13 | METADATA_MACSEC, |
14 | METADATA_XFRM, |
15 | }; |
16 | |
17 | struct hw_port_info { |
18 | struct net_device *lower_dev; |
19 | u32 port_id; |
20 | }; |
21 | |
22 | struct macsec_info { |
23 | sci_t sci; |
24 | }; |
25 | |
26 | struct xfrm_md_info { |
27 | u32 if_id; |
28 | int link; |
29 | struct dst_entry *dst_orig; |
30 | }; |
31 | |
32 | struct metadata_dst { |
33 | struct dst_entry dst; |
34 | enum metadata_type type; |
35 | union { |
36 | struct ip_tunnel_info tun_info; |
37 | struct hw_port_info port_info; |
38 | struct macsec_info macsec_info; |
39 | struct xfrm_md_info xfrm_info; |
40 | } u; |
41 | }; |
42 | |
43 | static inline struct metadata_dst *skb_metadata_dst(const struct sk_buff *skb) |
44 | { |
45 | struct metadata_dst *md_dst = (struct metadata_dst *) skb_dst(skb); |
46 | |
47 | if (md_dst && md_dst->dst.flags & DST_METADATA) |
48 | return md_dst; |
49 | |
50 | return NULL; |
51 | } |
52 | |
53 | static inline struct ip_tunnel_info * |
54 | skb_tunnel_info(const struct sk_buff *skb) |
55 | { |
56 | struct metadata_dst *md_dst = skb_metadata_dst(skb); |
57 | struct dst_entry *dst; |
58 | |
59 | if (md_dst && md_dst->type == METADATA_IP_TUNNEL) |
60 | return &md_dst->u.tun_info; |
61 | |
62 | dst = skb_dst(skb); |
63 | if (dst && dst->lwtstate && |
64 | (dst->lwtstate->type == LWTUNNEL_ENCAP_IP || |
65 | dst->lwtstate->type == LWTUNNEL_ENCAP_IP6)) |
66 | return lwt_tun_info(lwtstate: dst->lwtstate); |
67 | |
68 | return NULL; |
69 | } |
70 | |
71 | static inline struct xfrm_md_info *lwt_xfrm_info(struct lwtunnel_state *lwt) |
72 | { |
73 | return (struct xfrm_md_info *)lwt->data; |
74 | } |
75 | |
76 | static inline struct xfrm_md_info *skb_xfrm_md_info(const struct sk_buff *skb) |
77 | { |
78 | struct metadata_dst *md_dst = skb_metadata_dst(skb); |
79 | struct dst_entry *dst; |
80 | |
81 | if (md_dst && md_dst->type == METADATA_XFRM) |
82 | return &md_dst->u.xfrm_info; |
83 | |
84 | dst = skb_dst(skb); |
85 | if (dst && dst->lwtstate && |
86 | dst->lwtstate->type == LWTUNNEL_ENCAP_XFRM) |
87 | return lwt_xfrm_info(lwt: dst->lwtstate); |
88 | |
89 | return NULL; |
90 | } |
91 | |
92 | static inline bool skb_valid_dst(const struct sk_buff *skb) |
93 | { |
94 | struct dst_entry *dst = skb_dst(skb); |
95 | |
96 | return dst && !(dst->flags & DST_METADATA); |
97 | } |
98 | |
99 | static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a, |
100 | const struct sk_buff *skb_b) |
101 | { |
102 | const struct metadata_dst *a, *b; |
103 | |
104 | if (!(skb_a->_skb_refdst | skb_b->_skb_refdst)) |
105 | return 0; |
106 | |
107 | a = (const struct metadata_dst *) skb_dst(skb: skb_a); |
108 | b = (const struct metadata_dst *) skb_dst(skb: skb_b); |
109 | |
110 | if (!a != !b || a->type != b->type) |
111 | return 1; |
112 | |
113 | switch (a->type) { |
114 | case METADATA_HW_PORT_MUX: |
115 | return memcmp(p: &a->u.port_info, q: &b->u.port_info, |
116 | size: sizeof(a->u.port_info)); |
117 | case METADATA_IP_TUNNEL: |
118 | return memcmp(p: &a->u.tun_info, q: &b->u.tun_info, |
119 | size: sizeof(a->u.tun_info) + |
120 | a->u.tun_info.options_len); |
121 | case METADATA_MACSEC: |
122 | return memcmp(p: &a->u.macsec_info, q: &b->u.macsec_info, |
123 | size: sizeof(a->u.macsec_info)); |
124 | case METADATA_XFRM: |
125 | return memcmp(p: &a->u.xfrm_info, q: &b->u.xfrm_info, |
126 | size: sizeof(a->u.xfrm_info)); |
127 | default: |
128 | return 1; |
129 | } |
130 | } |
131 | |
132 | void metadata_dst_free(struct metadata_dst *); |
133 | struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type, |
134 | gfp_t flags); |
135 | void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst); |
136 | struct metadata_dst __percpu * |
137 | metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags); |
138 | |
139 | static inline struct metadata_dst *tun_rx_dst(int md_size) |
140 | { |
141 | struct metadata_dst *tun_dst; |
142 | |
143 | tun_dst = metadata_dst_alloc(optslen: md_size, type: METADATA_IP_TUNNEL, GFP_ATOMIC); |
144 | if (!tun_dst) |
145 | return NULL; |
146 | |
147 | tun_dst->u.tun_info.options_len = 0; |
148 | tun_dst->u.tun_info.mode = 0; |
149 | return tun_dst; |
150 | } |
151 | |
152 | static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb) |
153 | { |
154 | struct metadata_dst *md_dst = skb_metadata_dst(skb); |
155 | int md_size; |
156 | struct metadata_dst *new_md; |
157 | |
158 | if (!md_dst || md_dst->type != METADATA_IP_TUNNEL) |
159 | return ERR_PTR(error: -EINVAL); |
160 | |
161 | md_size = md_dst->u.tun_info.options_len; |
162 | new_md = metadata_dst_alloc(optslen: md_size, type: METADATA_IP_TUNNEL, GFP_ATOMIC); |
163 | if (!new_md) |
164 | return ERR_PTR(error: -ENOMEM); |
165 | |
166 | memcpy(&new_md->u.tun_info, &md_dst->u.tun_info, |
167 | sizeof(struct ip_tunnel_info) + md_size); |
168 | #ifdef CONFIG_DST_CACHE |
169 | /* Unclone the dst cache if there is one */ |
170 | if (new_md->u.tun_info.dst_cache.cache) { |
171 | int ret; |
172 | |
173 | ret = dst_cache_init(dst_cache: &new_md->u.tun_info.dst_cache, GFP_ATOMIC); |
174 | if (ret) { |
175 | metadata_dst_free(new_md); |
176 | return ERR_PTR(error: ret); |
177 | } |
178 | } |
179 | #endif |
180 | |
181 | skb_dst_drop(skb); |
182 | skb_dst_set(skb, dst: &new_md->dst); |
183 | return new_md; |
184 | } |
185 | |
186 | static inline struct ip_tunnel_info *skb_tunnel_info_unclone(struct sk_buff *skb) |
187 | { |
188 | struct metadata_dst *dst; |
189 | |
190 | dst = tun_dst_unclone(skb); |
191 | if (IS_ERR(ptr: dst)) |
192 | return NULL; |
193 | |
194 | return &dst->u.tun_info; |
195 | } |
196 | |
197 | static inline struct metadata_dst *__ip_tun_set_dst(__be32 saddr, |
198 | __be32 daddr, |
199 | __u8 tos, __u8 ttl, |
200 | __be16 tp_dst, |
201 | __be16 flags, |
202 | __be64 tunnel_id, |
203 | int md_size) |
204 | { |
205 | struct metadata_dst *tun_dst; |
206 | |
207 | tun_dst = tun_rx_dst(md_size); |
208 | if (!tun_dst) |
209 | return NULL; |
210 | |
211 | ip_tunnel_key_init(key: &tun_dst->u.tun_info.key, |
212 | saddr, daddr, tos, ttl, |
213 | label: 0, tp_src: 0, tp_dst, tun_id: tunnel_id, tun_flags: flags); |
214 | return tun_dst; |
215 | } |
216 | |
217 | static inline struct metadata_dst *ip_tun_rx_dst(struct sk_buff *skb, |
218 | __be16 flags, |
219 | __be64 tunnel_id, |
220 | int md_size) |
221 | { |
222 | const struct iphdr *iph = ip_hdr(skb); |
223 | |
224 | return __ip_tun_set_dst(saddr: iph->saddr, daddr: iph->daddr, tos: iph->tos, ttl: iph->ttl, |
225 | tp_dst: 0, flags, tunnel_id, md_size); |
226 | } |
227 | |
228 | static inline struct metadata_dst *__ipv6_tun_set_dst(const struct in6_addr *saddr, |
229 | const struct in6_addr *daddr, |
230 | __u8 tos, __u8 ttl, |
231 | __be16 tp_dst, |
232 | __be32 label, |
233 | __be16 flags, |
234 | __be64 tunnel_id, |
235 | int md_size) |
236 | { |
237 | struct metadata_dst *tun_dst; |
238 | struct ip_tunnel_info *info; |
239 | |
240 | tun_dst = tun_rx_dst(md_size); |
241 | if (!tun_dst) |
242 | return NULL; |
243 | |
244 | info = &tun_dst->u.tun_info; |
245 | info->mode = IP_TUNNEL_INFO_IPV6; |
246 | info->key.tun_flags = flags; |
247 | info->key.tun_id = tunnel_id; |
248 | info->key.tp_src = 0; |
249 | info->key.tp_dst = tp_dst; |
250 | |
251 | info->key.u.ipv6.src = *saddr; |
252 | info->key.u.ipv6.dst = *daddr; |
253 | |
254 | info->key.tos = tos; |
255 | info->key.ttl = ttl; |
256 | info->key.label = label; |
257 | |
258 | return tun_dst; |
259 | } |
260 | |
261 | static inline struct metadata_dst *ipv6_tun_rx_dst(struct sk_buff *skb, |
262 | __be16 flags, |
263 | __be64 tunnel_id, |
264 | int md_size) |
265 | { |
266 | const struct ipv6hdr *ip6h = ipv6_hdr(skb); |
267 | |
268 | return __ipv6_tun_set_dst(saddr: &ip6h->saddr, daddr: &ip6h->daddr, |
269 | tos: ipv6_get_dsfield(ipv6h: ip6h), ttl: ip6h->hop_limit, |
270 | tp_dst: 0, label: ip6_flowlabel(hdr: ip6h), flags, tunnel_id, |
271 | md_size); |
272 | } |
273 | #endif /* __NET_DST_METADATA_H */ |
274 | |