History

bpf: Add IPv6 Segment Routing helpers

The BPF seg6local hook should be powerful enough to enable users to implement most of the use-cases one could think of. After some thinking, we figured out that the following actions should be possible on a SRv6 packet, requiring 3 specific helpers : - bpf_lwt_seg6_store_bytes: Modify non-sensitive fields of the SRH - bpf_lwt_seg6_adjust_srh: Allow to grow or shrink a SRH (to add/delete TLVs) - bpf_lwt_seg6_action: Apply some SRv6 network programming actions (specifically End.X, End.T, End.B6 and End.B6.Encap) The specifications of these helpers are provided in the patch (see include/uapi/linux/bpf.h). The non-sensitive fields of the SRH are the following : flags, tag and TLVs. The other fields can not be modified, to maintain the SRH integrity. Flags, tag and TLVs can easily be modified as their validity can be checked afterwards via seg6_validate_srh. It is not allowed to modify the segments directly. If one wants to add segments on the path, he should stack a new SRH using the End.B6 action via bpf_lwt_seg6_action. Growing, shrinking or editing TLVs via the helpers will flag the SRH as invalid, and it will have to be re-validated before re-entering the IPv6 layer. This flag is stored in a per-CPU buffer, along with the current header length in bytes. Storing the SRH len in bytes in the control block is mandatory when using bpf_lwt_seg6_adjust_srh. The Header Ext. Length field contains the SRH len rounded to 8 bytes (a padding TLV can be inserted to ensure the 8-bytes boundary). When adding/deleting TLVs within the BPF program, the SRH may temporary be in an invalid state where its length cannot be rounded to 8 bytes without remainder, hence the need to store the length in bytes separately. The caller of the BPF program can then ensure that the SRH's final length is valid using this value. Again, a final SRH modified by a BPF program which doesn’t respect the 8-bytes boundary will be discarded as it will be considered as invalid. Finally, a fourth helper is provided, bpf_lwt_push_encap, which is available from the LWT BPF IN hook, but not from the seg6local BPF one. This helper allows to encapsulate a Segment Routing Header (either with a new outer IPv6 header, or by inlining it directly in the existing IPv6 header) into a non-SRv6 packet. This helper is required if we want to offer the possibility to dynamically encapsulate a SRH for non-SRv6 packet, as the BPF seg6local hook only works on traffic already containing a SRH. This is the BPF equivalent of the seg6 LWT infrastructure, which achieves the same purpose but with a static SRH per route. These helpers require CONFIG_IPV6=y (and not =m).

Mathieu Xhonneux committed 8 years ago

Commit fe94cc21 parent 1c1e7615 files · +372 −24Browse files

Unified · Split

5 files
  1. include/net/seg6_local.h +8 −0
  2. include/uapi/linux/bpf.h +95 −1
  3. net/core/filter.c +262 −23
  4. net/ipv6/Kconfig +5 −0
  5. net/ipv6/seg6_local.c +2 −0
include/net/seg6_local.h+8−0
@@ -15,10 +15,18 @@ · 14 unmodified lines
1515 #ifndef _NET_SEG6_LOCAL_H
1616 #define _NET_SEG6_LOCAL_H
1717
18+#include <linux/percpu.h>
1819 #include <linux/net.h>
1920 #include <linux/ipv6.h>
2021
2122 extern int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
2223 u32 tbl_id);
2324
25+struct seg6_bpf_srh_state {
26+ bool valid;
27+ u16 hdrlen;
28+};
29+
30+DECLARE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
31+
2432 #endif
include/uapi/linux/bpf.h+95−1
@@ -1902,6 +1902,90 @@ union bpf_attr { · 1901 unmodified lines
19021902 * egress otherwise). This is the only flag supported for now.
19031903 * Return
19041904 * **SK_PASS** on success, or **SK_DROP** on error.
1905+ *
1906+ * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
1907+ * Description
1908+ * Encapsulate the packet associated to *skb* within a Layer 3
1909+ * protocol header. This header is provided in the buffer at
1910+ * address *hdr*, with *len* its size in bytes. *type* indicates
1911+ * the protocol of the header and can be one of:
1912+ *
1913+ * **BPF_LWT_ENCAP_SEG6**
1914+ * IPv6 encapsulation with Segment Routing Header
1915+ * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
1916+ * the IPv6 header is computed by the kernel.
1917+ * **BPF_LWT_ENCAP_SEG6_INLINE**
1918+ * Only works if *skb* contains an IPv6 packet. Insert a
1919+ * Segment Routing Header (**struct ipv6_sr_hdr**) inside
1920+ * the IPv6 header.
1921+ *
1922+ * A call to this helper is susceptible to change the underlaying
1923+ * packet buffer. Therefore, at load time, all checks on pointers
1924+ * previously done by the verifier are invalidated and must be
1925+ * performed again, if the helper is used in combination with
1926+ * direct packet access.
1927+ * Return
1928+ * 0 on success, or a negative error in case of failure.
1929+ *
1930+ * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len)
1931+ * Description
1932+ * Store *len* bytes from address *from* into the packet
1933+ * associated to *skb*, at *offset*. Only the flags, tag and TLVs
1934+ * inside the outermost IPv6 Segment Routing Header can be
1935+ * modified through this helper.
1936+ *
1937+ * A call to this helper is susceptible to change the underlaying
1938+ * packet buffer. Therefore, at load time, all checks on pointers
1939+ * previously done by the verifier are invalidated and must be
1940+ * performed again, if the helper is used in combination with
1941+ * direct packet access.
1942+ * Return
1943+ * 0 on success, or a negative error in case of failure.
1944+ *
1945+ * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta)
1946+ * Description
1947+ * Adjust the size allocated to TLVs in the outermost IPv6
1948+ * Segment Routing Header contained in the packet associated to
1949+ * *skb*, at position *offset* by *delta* bytes. Only offsets
1950+ * after the segments are accepted. *delta* can be as well
1951+ * positive (growing) as negative (shrinking).
1952+ *
1953+ * A call to this helper is susceptible to change the underlaying
1954+ * packet buffer. Therefore, at load time, all checks on pointers
1955+ * previously done by the verifier are invalidated and must be
1956+ * performed again, if the helper is used in combination with
1957+ * direct packet access.
1958+ * Return
1959+ * 0 on success, or a negative error in case of failure.
1960+ *
1961+ * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len)
1962+ * Description
1963+ * Apply an IPv6 Segment Routing action of type *action* to the
1964+ * packet associated to *skb*. Each action takes a parameter
1965+ * contained at address *param*, and of length *param_len* bytes.
1966+ * *action* can be one of:
1967+ *
1968+ * **SEG6_LOCAL_ACTION_END_X**
1969+ * End.X action: Endpoint with Layer-3 cross-connect.
1970+ * Type of *param*: **struct in6_addr**.
1971+ * **SEG6_LOCAL_ACTION_END_T**
1972+ * End.T action: Endpoint with specific IPv6 table lookup.
1973+ * Type of *param*: **int**.
1974+ * **SEG6_LOCAL_ACTION_END_B6**
1975+ * End.B6 action: Endpoint bound to an SRv6 policy.
1976+ * Type of param: **struct ipv6_sr_hdr**.
1977+ * **SEG6_LOCAL_ACTION_END_B6_ENCAP**
1978+ * End.B6.Encap action: Endpoint bound to an SRv6
1979+ * encapsulation policy.
1980+ * Type of param: **struct ipv6_sr_hdr**.
1981+ *
1982+ * A call to this helper is susceptible to change the underlaying
1983+ * packet buffer. Therefore, at load time, all checks on pointers
1984+ * previously done by the verifier are invalidated and must be
1985+ * performed again, if the helper is used in combination with
1986+ * direct packet access.
1987+ * Return
1988+ * 0 on success, or a negative error in case of failure.
19051989 */
19061990 #define __BPF_FUNC_MAPPER(FN) \
19071991 FN(unspec), \
@@ -1976,7 +2060,11 @@ union bpf_attr { · 68 unmodified lines
19762060 FN(fib_lookup), \
19772061 FN(sock_hash_update), \
19782062 FN(msg_redirect_hash), \
1979− FN(sk_redirect_hash),
2063+ FN(sk_redirect_hash), \
2064+ FN(lwt_push_encap), \
2065+ FN(lwt_seg6_store_bytes), \
2066+ FN(lwt_seg6_adjust_srh), \
2067+ FN(lwt_seg6_action),
19802068
19812069 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
19822070 * function eBPF program intends to call
@@ -2043,6 +2131,12 @@ enum bpf_hdr_start_off { · 60 unmodified lines
20432131 BPF_HDR_START_NET,
20442132 };
20452133
2134+/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
2135+enum bpf_lwt_encap_mode {
2136+ BPF_LWT_ENCAP_SEG6,
2137+ BPF_LWT_ENCAP_SEG6_INLINE
2138+};
2139+
20462140 /* user accessible mirror of in-kernel sk_buff.
20472141 * new fields can only be added to the end of this structure
20482142 */
net/core/filter.c+262−23
@@ -64,6 +64,10 @@ · 63 unmodified lines
6464 #include <net/ip_fib.h>
6565 #include <net/flow.h>
6666 #include <net/arp.h>
67+#include <net/ipv6.h>
68+#include <linux/seg6_local.h>
69+#include <net/seg6.h>
70+#include <net/seg6_local.h>
6771
6872 /**
6973 * sk_filter_trim_cap - run a packet through a socket filter
@@ -3363,28 +3367,6 @@ static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { · 3293 unmodified lines
33633367 .arg3_type = ARG_ANYTHING,
33643368 };
33653369
3366−bool bpf_helper_changes_pkt_data(void *func)
3367−{
3368− if (func == bpf_skb_vlan_push ||
3369− func == bpf_skb_vlan_pop ||
3370− func == bpf_skb_store_bytes ||
3371− func == bpf_skb_change_proto ||
3372− func == bpf_skb_change_head ||
3373− func == bpf_skb_change_tail ||
3374− func == bpf_skb_adjust_room ||
3375− func == bpf_skb_pull_data ||
3376− func == bpf_clone_redirect ||
3377− func == bpf_l3_csum_replace ||
3378− func == bpf_l4_csum_replace ||
3379− func == bpf_xdp_adjust_head ||
3380− func == bpf_xdp_adjust_meta ||
3381− func == bpf_msg_pull_data ||
3382− func == bpf_xdp_adjust_tail)
3383− return true;
3384−
3385− return false;
3386−}
3387−
33883370 static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,
33893371 unsigned long off, unsigned long len)
33903372 {
@@ -4360,6 +4342,264 @@ static const struct bpf_func_proto bpf_skb_fib_lookup_proto = { · 969 unmodified lines
43604342 .arg4_type = ARG_ANYTHING,
43614343 };
43624344
4345+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4346+static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
4347+{
4348+ int err;
4349+ struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)hdr;
4350+
4351+ if (!seg6_validate_srh(srh, len))
4352+ return -EINVAL;
4353+
4354+ switch (type) {
4355+ case BPF_LWT_ENCAP_SEG6_INLINE:
4356+ if (skb->protocol != htons(ETH_P_IPV6))
4357+ return -EBADMSG;
4358+
4359+ err = seg6_do_srh_inline(skb, srh);
4360+ break;
4361+ case BPF_LWT_ENCAP_SEG6:
4362+ skb_reset_inner_headers(skb);
4363+ skb->encapsulation = 1;
4364+ err = seg6_do_srh_encap(skb, srh, IPPROTO_IPV6);
4365+ break;
4366+ default:
4367+ return -EINVAL;
4368+ }
4369+
4370+ bpf_compute_data_pointers(skb);
4371+ if (err)
4372+ return err;
4373+
4374+ ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
4375+ skb_set_transport_header(skb, sizeof(struct ipv6hdr));
4376+
4377+ return seg6_lookup_nexthop(skb, NULL, 0);
4378+}
4379+#endif /* CONFIG_IPV6_SEG6_BPF */
4380+
4381+BPF_CALL_4(bpf_lwt_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,
4382+ u32, len)
4383+{
4384+ switch (type) {
4385+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4386+ case BPF_LWT_ENCAP_SEG6:
4387+ case BPF_LWT_ENCAP_SEG6_INLINE:
4388+ return bpf_push_seg6_encap(skb, type, hdr, len);
4389+#endif
4390+ default:
4391+ return -EINVAL;
4392+ }
4393+}
4394+
4395+static const struct bpf_func_proto bpf_lwt_push_encap_proto = {
4396+ .func = bpf_lwt_push_encap,
4397+ .gpl_only = false,
4398+ .ret_type = RET_INTEGER,
4399+ .arg1_type = ARG_PTR_TO_CTX,
4400+ .arg2_type = ARG_ANYTHING,
4401+ .arg3_type = ARG_PTR_TO_MEM,
4402+ .arg4_type = ARG_CONST_SIZE
4403+};
4404+
4405+BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset,
4406+ const void *, from, u32, len)
4407+{
4408+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4409+ struct seg6_bpf_srh_state *srh_state =
4410+ this_cpu_ptr(&seg6_bpf_srh_states);
4411+ void *srh_tlvs, *srh_end, *ptr;
4412+ struct ipv6_sr_hdr *srh;
4413+ int srhoff = 0;
4414+
4415+ if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4416+ return -EINVAL;
4417+
4418+ srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4419+ srh_tlvs = (void *)((char *)srh + ((srh->first_segment + 1) << 4));
4420+ srh_end = (void *)((char *)srh + sizeof(*srh) + srh_state->hdrlen);
4421+
4422+ ptr = skb->data + offset;
4423+ if (ptr >= srh_tlvs && ptr + len <= srh_end)
4424+ srh_state->valid = 0;
4425+ else if (ptr < (void *)&srh->flags ||
4426+ ptr + len > (void *)&srh->segments)
4427+ return -EFAULT;
4428+
4429+ if (unlikely(bpf_try_make_writable(skb, offset + len)))
4430+ return -EFAULT;
4431+
4432+ memcpy(skb->data + offset, from, len);
4433+ return 0;
4434+#else /* CONFIG_IPV6_SEG6_BPF */
4435+ return -EOPNOTSUPP;
4436+#endif
4437+}
4438+
4439+static const struct bpf_func_proto bpf_lwt_seg6_store_bytes_proto = {
4440+ .func = bpf_lwt_seg6_store_bytes,
4441+ .gpl_only = false,
4442+ .ret_type = RET_INTEGER,
4443+ .arg1_type = ARG_PTR_TO_CTX,
4444+ .arg2_type = ARG_ANYTHING,
4445+ .arg3_type = ARG_PTR_TO_MEM,
4446+ .arg4_type = ARG_CONST_SIZE
4447+};
4448+
4449+BPF_CALL_4(bpf_lwt_seg6_action, struct sk_buff *, skb,
4450+ u32, action, void *, param, u32, param_len)
4451+{
4452+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4453+ struct seg6_bpf_srh_state *srh_state =
4454+ this_cpu_ptr(&seg6_bpf_srh_states);
4455+ struct ipv6_sr_hdr *srh;
4456+ int srhoff = 0;
4457+ int err;
4458+
4459+ if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4460+ return -EINVAL;
4461+ srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4462+
4463+ if (!srh_state->valid) {
4464+ if (unlikely((srh_state->hdrlen & 7) != 0))
4465+ return -EBADMSG;
4466+
4467+ srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
4468+ if (unlikely(!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3)))
4469+ return -EBADMSG;
4470+
4471+ srh_state->valid = 1;
4472+ }
4473+
4474+ switch (action) {
4475+ case SEG6_LOCAL_ACTION_END_X:
4476+ if (param_len != sizeof(struct in6_addr))
4477+ return -EINVAL;
4478+ return seg6_lookup_nexthop(skb, (struct in6_addr *)param, 0);
4479+ case SEG6_LOCAL_ACTION_END_T:
4480+ if (param_len != sizeof(int))
4481+ return -EINVAL;
4482+ return seg6_lookup_nexthop(skb, NULL, *(int *)param);
4483+ case SEG6_LOCAL_ACTION_END_B6:
4484+ err = bpf_push_seg6_encap(skb, BPF_LWT_ENCAP_SEG6_INLINE,
4485+ param, param_len);
4486+ if (!err)
4487+ srh_state->hdrlen =
4488+ ((struct ipv6_sr_hdr *)param)->hdrlen << 3;
4489+ return err;
4490+ case SEG6_LOCAL_ACTION_END_B6_ENCAP:
4491+ err = bpf_push_seg6_encap(skb, BPF_LWT_ENCAP_SEG6,
4492+ param, param_len);
4493+ if (!err)
4494+ srh_state->hdrlen =
4495+ ((struct ipv6_sr_hdr *)param)->hdrlen << 3;
4496+ return err;
4497+ default:
4498+ return -EINVAL;
4499+ }
4500+#else /* CONFIG_IPV6_SEG6_BPF */
4501+ return -EOPNOTSUPP;
4502+#endif
4503+}
4504+
4505+static const struct bpf_func_proto bpf_lwt_seg6_action_proto = {
4506+ .func = bpf_lwt_seg6_action,
4507+ .gpl_only = false,
4508+ .ret_type = RET_INTEGER,
4509+ .arg1_type = ARG_PTR_TO_CTX,
4510+ .arg2_type = ARG_ANYTHING,
4511+ .arg3_type = ARG_PTR_TO_MEM,
4512+ .arg4_type = ARG_CONST_SIZE
4513+};
4514+
4515+BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
4516+ s32, len)
4517+{
4518+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4519+ struct seg6_bpf_srh_state *srh_state =
4520+ this_cpu_ptr(&seg6_bpf_srh_states);
4521+ void *srh_end, *srh_tlvs, *ptr;
4522+ struct ipv6_sr_hdr *srh;
4523+ struct ipv6hdr *hdr;
4524+ int srhoff = 0;
4525+ int ret;
4526+
4527+ if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4528+ return -EINVAL;
4529+ srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4530+
4531+ srh_tlvs = (void *)((unsigned char *)srh + sizeof(*srh) +
4532+ ((srh->first_segment + 1) << 4));
4533+ srh_end = (void *)((unsigned char *)srh + sizeof(*srh) +
4534+ srh_state->hdrlen);
4535+ ptr = skb->data + offset;
4536+
4537+ if (unlikely(ptr < srh_tlvs || ptr > srh_end))
4538+ return -EFAULT;
4539+ if (unlikely(len < 0 && (void *)((char *)ptr - len) > srh_end))
4540+ return -EFAULT;
4541+
4542+ if (len > 0) {
4543+ ret = skb_cow_head(skb, len);
4544+ if (unlikely(ret < 0))
4545+ return ret;
4546+
4547+ ret = bpf_skb_net_hdr_push(skb, offset, len);
4548+ } else {
4549+ ret = bpf_skb_net_hdr_pop(skb, offset, -1 * len);
4550+ }
4551+
4552+ bpf_compute_data_pointers(skb);
4553+ if (unlikely(ret < 0))
4554+ return ret;
4555+
4556+ hdr = (struct ipv6hdr *)skb->data;
4557+ hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
4558+
4559+ srh_state->hdrlen += len;
4560+ srh_state->valid = 0;
4561+ return 0;
4562+#else /* CONFIG_IPV6_SEG6_BPF */
4563+ return -EOPNOTSUPP;
4564+#endif
4565+}
4566+
4567+static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
4568+ .func = bpf_lwt_seg6_adjust_srh,
4569+ .gpl_only = false,
4570+ .ret_type = RET_INTEGER,
4571+ .arg1_type = ARG_PTR_TO_CTX,
4572+ .arg2_type = ARG_ANYTHING,
4573+ .arg3_type = ARG_ANYTHING,
4574+};
4575+
4576+bool bpf_helper_changes_pkt_data(void *func)
4577+{
4578+ if (func == bpf_skb_vlan_push ||
4579+ func == bpf_skb_vlan_pop ||
4580+ func == bpf_skb_store_bytes ||
4581+ func == bpf_skb_change_proto ||
4582+ func == bpf_skb_change_head ||
4583+ func == bpf_skb_change_tail ||
4584+ func == bpf_skb_adjust_room ||
4585+ func == bpf_skb_pull_data ||
4586+ func == bpf_clone_redirect ||
4587+ func == bpf_l3_csum_replace ||
4588+ func == bpf_l4_csum_replace ||
4589+ func == bpf_xdp_adjust_head ||
4590+ func == bpf_xdp_adjust_meta ||
4591+ func == bpf_msg_pull_data ||
4592+ func == bpf_xdp_adjust_tail ||
4593+ func == bpf_lwt_push_encap ||
4594+ func == bpf_lwt_seg6_store_bytes ||
4595+ func == bpf_lwt_seg6_adjust_srh ||
4596+ func == bpf_lwt_seg6_action
4597+ )
4598+ return true;
4599+
4600+ return false;
4601+}
4602+
43634603 static const struct bpf_func_proto *
43644604 bpf_base_func_proto(enum bpf_func_id func_id)
43654605 {
@@ -4774,7 +5014,6 @@ static bool lwt_is_valid_access(int off, int size, · 408 unmodified lines
47745014 return bpf_skb_is_valid_access(off, size, type, prog, info);
47755015 }
47765016
4777−
47785017 /* Attach type specific accesses */
47795018 static bool __sock_filter_check_attach_type(int off,
47805019 enum bpf_access_type access_type,
net/ipv6/Kconfig+5−0
@@ -329,4 +329,9 @@ config IPV6_SEG6_HMAC · 328 unmodified lines
329329
330330 If unsure, say N.
331331
332+config IPV6_SEG6_BPF
333+ def_bool y
334+ depends on IPV6_SEG6_LWTUNNEL
335+ depends on IPV6 = y
336+
332337 endif # IPV6
net/ipv6/seg6_local.c+2−0
@@ -449,6 +449,8 @@ drop: · 448 unmodified lines
449449 return err;
450450 }
451451
452+DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
453+
452454 static struct seg6_action_desc seg6_action_table[] = {
453455 {
454456 .action = SEG6_LOCAL_ACTION_END,