Message ID | 20200518154026.18059-9-elibr@mellanox.com |
---|---|
State | Changes Requested |
Headers | show |
Series | netdev datapath offload: Support IPv6 and VXLAN encap | expand |
On Mon, May 18, 2020 at 9:10 PM Eli Britstein <elibr@mellanox.com> wrote: > > For DPDK, there is the RAW_ENCAP attribute which gets raw buffer of the > encapsulation header. For specific protocol, such as vxlan, there is a > more specific attribute, VXLAN_ENCAP, which gets the parsed fields of > the outer header. In case tunnel type is vxlan, parse the header > and use the specific attribute, with fallback to RAW_ENCAP. > > Signed-off-by: Eli Britstein <elibr@mellanox.com> > Reviewed-by: Roni Bar Yanai <roniba@mellanox.com> > --- > lib/netdev-offload-dpdk.c | 123 +++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 121 insertions(+), 2 deletions(-) > > diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c > index 38f8f00ee..de313e40b 100644 > --- a/lib/netdev-offload-dpdk.c > +++ b/lib/netdev-offload-dpdk.c > @@ -359,6 +359,25 @@ dump_flow_pattern(struct ds *s, const struct rte_flow_item *item) > } else { > ds_put_cstr(s, " Mask = null\n"); > } > + } else if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) { > + const struct rte_flow_item_vxlan *vxlan_spec = item->spec; > + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; > + > + ds_put_cstr(s, "rte flow vxlan pattern:\n"); > + if (vxlan_spec) { > + ds_put_format(s, " Spec: flags=0x%x, vni=%"PRIu32"\n", > + vxlan_spec->flags, > + ntohl(*(ovs_be32 *)vxlan_spec->vni) >> 8); > + } else { > + ds_put_cstr(s, " Spec = null\n"); > + } > + if (vxlan_mask) { > + ds_put_format(s, " Mask: flags=0x%x, vni=0x%06"PRIx32"\n", > + vxlan_mask->flags, > + ntohl(*(ovs_be32 *)vxlan_mask->vni) >> 8); > + } else { > + ds_put_cstr(s, " Mask = null\n"); > + } > } else { > ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type); > } > @@ -486,6 +505,14 @@ dump_flow_action(struct ds *s, const struct rte_flow_action *actions) > } else { > ds_put_cstr(s, " Raw-encap = null\n"); > } > + } else if (actions->type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { > + const struct rte_flow_action_vxlan_encap *vxlan_encap = actions->conf; > + const struct rte_flow_item *items = vxlan_encap->definition; > + > + ds_put_cstr(s, "rte flow vxlan-encap action:\n"); > + while (items && items->type != RTE_FLOW_ITEM_TYPE_END) { > + dump_flow_pattern(s, items++); > + } > } else { > ds_put_format(s, "unknown rte flow action (%d)\n", actions->type); > } > @@ -1129,6 +1156,93 @@ parse_set_actions(struct flow_actions *actions, > return 0; > } > > +/* Maximum number of items in struct rte_flow_action_vxlan_encap. > + * ETH / IPv4(6) / UDP / VXLAN / END > + */ > +#define ACTION_VXLAN_ENCAP_ITEMS_NUM 5 Max number of items as per rte_flow.h is 6 (including vlan header). > + > +static int > +add_vxlan_encap_action(struct flow_actions *actions, > + const void *header) > +{ > + const struct eth_header *eth; > + const struct udp_header *udp; > + struct vxlan_data { > + struct rte_flow_action_vxlan_encap conf; > + struct rte_flow_item items[0]; > + } *vxlan_data; > + BUILD_ASSERT_DECL(offsetof(struct vxlan_data, conf) == 0); > + const void *vxlan; > + const void *l3; > + const void *l4; > + int field; > + > + vxlan_data = xzalloc(sizeof *vxlan_data + > + sizeof(struct rte_flow_item) * > + ACTION_VXLAN_ENCAP_ITEMS_NUM); > + field = 0; > + > + eth = header; Copy the header provided by OVS (nlattr) before passing it to RTE ? also the headers are being set directly as the 'spec' below, as opposed to rte_flow_item structures for each item below. This is ok since these are standard headers and so the formats match. Maybe type cast it to the respective rte_flow_item type at least while assigning to indicate it explicitly ? I'm fine with the rest of the changes in this patch. Thanks, -Harsha > + /* Ethernet */ > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_ETH; > + vxlan_data->items[field].spec = eth; > + vxlan_data->items[field].mask = &rte_flow_item_eth_mask; > + field++; > + > + l3 = eth + 1; > + /* IP */ > + if (eth->eth_type == htons(ETH_TYPE_IP)) { > + /* IPv4 */ > + const struct ip_header *ip = l3; > + > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV4; > + vxlan_data->items[field].spec = ip; > + vxlan_data->items[field].mask = &rte_flow_item_ipv4_mask; > + > + if (ip->ip_proto != IPPROTO_UDP) { > + goto err; > + } > + l4 = (ip + 1); > + } else if (eth->eth_type == htons(ETH_TYPE_IPV6)) { > + const struct ovs_16aligned_ip6_hdr *ip6 = l3; > + > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV6; > + vxlan_data->items[field].spec = ip6; > + vxlan_data->items[field].mask = &rte_flow_item_ipv6_mask; > + > + if (ip6->ip6_nxt != IPPROTO_UDP) { > + goto err; > + } > + l4 = (ip6 + 1); > + } else { > + goto err; > + } > + field++; > + > + udp = (const struct udp_header *)l4; > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_UDP; > + vxlan_data->items[field].spec = udp; > + vxlan_data->items[field].mask = &rte_flow_item_udp_mask; > + field++; > + > + vxlan = (udp + 1); > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_VXLAN; > + vxlan_data->items[field].spec = vxlan; > + vxlan_data->items[field].mask = &rte_flow_item_vxlan_mask; > + field++; > + > + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_END; > + > + vxlan_data->conf.definition = vxlan_data->items; > + > + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP, vxlan_data); > + > + return 0; > +err: > + free(vxlan_data); > + return -1; > +} > + > static int > parse_clone_actions(struct netdev *netdev, > struct flow_actions *actions, > @@ -1143,9 +1257,14 @@ parse_clone_actions(struct netdev *netdev, > > if (clone_type == OVS_ACTION_ATTR_TUNNEL_PUSH) { > const struct ovs_action_push_tnl *tnl_push = nl_attr_get(ca); > - struct rte_flow_action_raw_encap *raw_encap = > - xzalloc(sizeof *raw_encap); > + struct rte_flow_action_raw_encap *raw_encap; > + > + if (tnl_push->tnl_type == OVS_VPORT_TYPE_VXLAN && > + !add_vxlan_encap_action(actions, tnl_push->header)) { > + continue; > + } > > + raw_encap = xzalloc(sizeof *raw_encap); > raw_encap->data = (uint8_t *)tnl_push->header; > raw_encap->preserve = NULL; > raw_encap->size = tnl_push->header_len; > -- > 2.14.5 >
On 5/20/2020 12:12 PM, Sriharsha Basavapatna wrote: > On Mon, May 18, 2020 at 9:10 PM Eli Britstein <elibr@mellanox.com> wrote: >> For DPDK, there is the RAW_ENCAP attribute which gets raw buffer of the >> encapsulation header. For specific protocol, such as vxlan, there is a >> more specific attribute, VXLAN_ENCAP, which gets the parsed fields of >> the outer header. In case tunnel type is vxlan, parse the header >> and use the specific attribute, with fallback to RAW_ENCAP. >> >> Signed-off-by: Eli Britstein <elibr@mellanox.com> >> Reviewed-by: Roni Bar Yanai <roniba@mellanox.com> >> --- >> lib/netdev-offload-dpdk.c | 123 +++++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 121 insertions(+), 2 deletions(-) >> >> diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c >> index 38f8f00ee..de313e40b 100644 >> --- a/lib/netdev-offload-dpdk.c >> +++ b/lib/netdev-offload-dpdk.c >> @@ -359,6 +359,25 @@ dump_flow_pattern(struct ds *s, const struct rte_flow_item *item) >> } else { >> ds_put_cstr(s, " Mask = null\n"); >> } >> + } else if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) { >> + const struct rte_flow_item_vxlan *vxlan_spec = item->spec; >> + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; >> + >> + ds_put_cstr(s, "rte flow vxlan pattern:\n"); >> + if (vxlan_spec) { >> + ds_put_format(s, " Spec: flags=0x%x, vni=%"PRIu32"\n", >> + vxlan_spec->flags, >> + ntohl(*(ovs_be32 *)vxlan_spec->vni) >> 8); >> + } else { >> + ds_put_cstr(s, " Spec = null\n"); >> + } >> + if (vxlan_mask) { >> + ds_put_format(s, " Mask: flags=0x%x, vni=0x%06"PRIx32"\n", >> + vxlan_mask->flags, >> + ntohl(*(ovs_be32 *)vxlan_mask->vni) >> 8); >> + } else { >> + ds_put_cstr(s, " Mask = null\n"); >> + } >> } else { >> ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type); >> } >> @@ -486,6 +505,14 @@ dump_flow_action(struct ds *s, const struct rte_flow_action *actions) >> } else { >> ds_put_cstr(s, " Raw-encap = null\n"); >> } >> + } else if (actions->type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { >> + const struct rte_flow_action_vxlan_encap *vxlan_encap = actions->conf; >> + const struct rte_flow_item *items = vxlan_encap->definition; >> + >> + ds_put_cstr(s, "rte flow vxlan-encap action:\n"); >> + while (items && items->type != RTE_FLOW_ITEM_TYPE_END) { >> + dump_flow_pattern(s, items++); >> + } >> } else { >> ds_put_format(s, "unknown rte flow action (%d)\n", actions->type); >> } >> @@ -1129,6 +1156,93 @@ parse_set_actions(struct flow_actions *actions, >> return 0; >> } >> >> +/* Maximum number of items in struct rte_flow_action_vxlan_encap. >> + * ETH / IPv4(6) / UDP / VXLAN / END >> + */ >> +#define ACTION_VXLAN_ENCAP_ITEMS_NUM 5 > Max number of items as per rte_flow.h is 6 (including vlan header). That's right from DPDK point of view. However, I didn't see OVS-DPDK supports that. I followed format_odp_tnl_push_header function in lib/odp-util.c:8591. If VLAN is supported somehow, it will fallback to the raw_encap method. >> + >> +static int >> +add_vxlan_encap_action(struct flow_actions *actions, >> + const void *header) >> +{ >> + const struct eth_header *eth; >> + const struct udp_header *udp; >> + struct vxlan_data { >> + struct rte_flow_action_vxlan_encap conf; >> + struct rte_flow_item items[0]; >> + } *vxlan_data; >> + BUILD_ASSERT_DECL(offsetof(struct vxlan_data, conf) == 0); >> + const void *vxlan; >> + const void *l3; >> + const void *l4; >> + int field; >> + >> + vxlan_data = xzalloc(sizeof *vxlan_data + >> + sizeof(struct rte_flow_item) * >> + ACTION_VXLAN_ENCAP_ITEMS_NUM); >> + field = 0; >> + >> + eth = header; > Copy the header provided by OVS (nlattr) before passing it to RTE ? There is no need. See comment in previous commit. > also the headers are being set directly as the 'spec' below, as > opposed to rte_flow_item structures for each item below. This is ok > since these are standard headers and so the formats match. Maybe type > cast it to the respective rte_flow_item type at least while assigning > to indicate it explicitly ? I'm fine with the rest of the changes in > this patch. "spec" and "mask" are "const void *" by DPDK. what will such casting serve? > > Thanks, > -Harsha > > >> + /* Ethernet */ >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_ETH; >> + vxlan_data->items[field].spec = eth; >> + vxlan_data->items[field].mask = &rte_flow_item_eth_mask; >> + field++; >> + >> + l3 = eth + 1; >> + /* IP */ >> + if (eth->eth_type == htons(ETH_TYPE_IP)) { >> + /* IPv4 */ >> + const struct ip_header *ip = l3; >> + >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV4; >> + vxlan_data->items[field].spec = ip; >> + vxlan_data->items[field].mask = &rte_flow_item_ipv4_mask; >> + >> + if (ip->ip_proto != IPPROTO_UDP) { >> + goto err; >> + } >> + l4 = (ip + 1); >> + } else if (eth->eth_type == htons(ETH_TYPE_IPV6)) { >> + const struct ovs_16aligned_ip6_hdr *ip6 = l3; >> + >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV6; >> + vxlan_data->items[field].spec = ip6; >> + vxlan_data->items[field].mask = &rte_flow_item_ipv6_mask; >> + >> + if (ip6->ip6_nxt != IPPROTO_UDP) { >> + goto err; >> + } >> + l4 = (ip6 + 1); >> + } else { >> + goto err; >> + } >> + field++; >> + >> + udp = (const struct udp_header *)l4; >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_UDP; >> + vxlan_data->items[field].spec = udp; >> + vxlan_data->items[field].mask = &rte_flow_item_udp_mask; >> + field++; >> + >> + vxlan = (udp + 1); >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_VXLAN; >> + vxlan_data->items[field].spec = vxlan; >> + vxlan_data->items[field].mask = &rte_flow_item_vxlan_mask; >> + field++; >> + >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_END; >> + >> + vxlan_data->conf.definition = vxlan_data->items; >> + >> + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP, vxlan_data); >> + >> + return 0; >> +err: >> + free(vxlan_data); >> + return -1; >> +} >> + >> static int >> parse_clone_actions(struct netdev *netdev, >> struct flow_actions *actions, >> @@ -1143,9 +1257,14 @@ parse_clone_actions(struct netdev *netdev, >> >> if (clone_type == OVS_ACTION_ATTR_TUNNEL_PUSH) { >> const struct ovs_action_push_tnl *tnl_push = nl_attr_get(ca); >> - struct rte_flow_action_raw_encap *raw_encap = >> - xzalloc(sizeof *raw_encap); >> + struct rte_flow_action_raw_encap *raw_encap; >> + >> + if (tnl_push->tnl_type == OVS_VPORT_TYPE_VXLAN && >> + !add_vxlan_encap_action(actions, tnl_push->header)) { >> + continue; >> + } >> >> + raw_encap = xzalloc(sizeof *raw_encap); >> raw_encap->data = (uint8_t *)tnl_push->header; >> raw_encap->preserve = NULL; >> raw_encap->size = tnl_push->header_len; >> -- >> 2.14.5 >>
On Wed, May 20, 2020 at 3:38 PM Eli Britstein <elibr@mellanox.com> wrote: > > > On 5/20/2020 12:12 PM, Sriharsha Basavapatna wrote: > > On Mon, May 18, 2020 at 9:10 PM Eli Britstein <elibr@mellanox.com> wrote: > >> For DPDK, there is the RAW_ENCAP attribute which gets raw buffer of the > >> encapsulation header. For specific protocol, such as vxlan, there is a > >> more specific attribute, VXLAN_ENCAP, which gets the parsed fields of > >> the outer header. In case tunnel type is vxlan, parse the header > >> and use the specific attribute, with fallback to RAW_ENCAP. > >> > >> Signed-off-by: Eli Britstein <elibr@mellanox.com> > >> Reviewed-by: Roni Bar Yanai <roniba@mellanox.com> > >> --- > >> lib/netdev-offload-dpdk.c | 123 +++++++++++++++++++++++++++++++++++++++++++++- > >> 1 file changed, 121 insertions(+), 2 deletions(-) > >> > >> diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c > >> index 38f8f00ee..de313e40b 100644 > >> --- a/lib/netdev-offload-dpdk.c > >> +++ b/lib/netdev-offload-dpdk.c > >> @@ -359,6 +359,25 @@ dump_flow_pattern(struct ds *s, const struct rte_flow_item *item) > >> } else { > >> ds_put_cstr(s, " Mask = null\n"); > >> } > >> + } else if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) { > >> + const struct rte_flow_item_vxlan *vxlan_spec = item->spec; > >> + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; > >> + > >> + ds_put_cstr(s, "rte flow vxlan pattern:\n"); > >> + if (vxlan_spec) { > >> + ds_put_format(s, " Spec: flags=0x%x, vni=%"PRIu32"\n", > >> + vxlan_spec->flags, > >> + ntohl(*(ovs_be32 *)vxlan_spec->vni) >> 8); > >> + } else { > >> + ds_put_cstr(s, " Spec = null\n"); > >> + } > >> + if (vxlan_mask) { > >> + ds_put_format(s, " Mask: flags=0x%x, vni=0x%06"PRIx32"\n", > >> + vxlan_mask->flags, > >> + ntohl(*(ovs_be32 *)vxlan_mask->vni) >> 8); > >> + } else { > >> + ds_put_cstr(s, " Mask = null\n"); > >> + } > >> } else { > >> ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type); > >> } > >> @@ -486,6 +505,14 @@ dump_flow_action(struct ds *s, const struct rte_flow_action *actions) > >> } else { > >> ds_put_cstr(s, " Raw-encap = null\n"); > >> } > >> + } else if (actions->type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { > >> + const struct rte_flow_action_vxlan_encap *vxlan_encap = actions->conf; > >> + const struct rte_flow_item *items = vxlan_encap->definition; > >> + > >> + ds_put_cstr(s, "rte flow vxlan-encap action:\n"); > >> + while (items && items->type != RTE_FLOW_ITEM_TYPE_END) { > >> + dump_flow_pattern(s, items++); > >> + } > >> } else { > >> ds_put_format(s, "unknown rte flow action (%d)\n", actions->type); > >> } > >> @@ -1129,6 +1156,93 @@ parse_set_actions(struct flow_actions *actions, > >> return 0; > >> } > >> > >> +/* Maximum number of items in struct rte_flow_action_vxlan_encap. > >> + * ETH / IPv4(6) / UDP / VXLAN / END > >> + */ > >> +#define ACTION_VXLAN_ENCAP_ITEMS_NUM 5 > > Max number of items as per rte_flow.h is 6 (including vlan header). > > That's right from DPDK point of view. However, I didn't see OVS-DPDK > supports that. > > I followed format_odp_tnl_push_header function in lib/odp-util.c:8591. > If VLAN is supported somehow, it will fallback to the raw_encap method. > > >> + > >> +static int > >> +add_vxlan_encap_action(struct flow_actions *actions, > >> + const void *header) > >> +{ > >> + const struct eth_header *eth; > >> + const struct udp_header *udp; > >> + struct vxlan_data { > >> + struct rte_flow_action_vxlan_encap conf; > >> + struct rte_flow_item items[0]; > >> + } *vxlan_data; > >> + BUILD_ASSERT_DECL(offsetof(struct vxlan_data, conf) == 0); > >> + const void *vxlan; > >> + const void *l3; > >> + const void *l4; > >> + int field; > >> + > >> + vxlan_data = xzalloc(sizeof *vxlan_data + > >> + sizeof(struct rte_flow_item) * > >> + ACTION_VXLAN_ENCAP_ITEMS_NUM); > >> + field = 0; > >> + > >> + eth = header; > > Copy the header provided by OVS (nlattr) before passing it to RTE ? > There is no need. See comment in previous commit. > > also the headers are being set directly as the 'spec' below, as > > opposed to rte_flow_item structures for each item below. This is ok > > since these are standard headers and so the formats match. Maybe type > > cast it to the respective rte_flow_item type at least while assigning > > to indicate it explicitly ? I'm fine with the rest of the changes in > > this patch. > "spec" and "mask" are "const void *" by DPDK. what will such casting serve? I was thinking they are pointers to specific data types (e.g rte_flow_item_eth *), no need to change it then. > > > > Thanks, > > -Harsha > > > > > >> + /* Ethernet */ > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_ETH; > >> + vxlan_data->items[field].spec = eth; > >> + vxlan_data->items[field].mask = &rte_flow_item_eth_mask; > >> + field++; > >> + > >> + l3 = eth + 1; > >> + /* IP */ > >> + if (eth->eth_type == htons(ETH_TYPE_IP)) { > >> + /* IPv4 */ > >> + const struct ip_header *ip = l3; > >> + > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV4; > >> + vxlan_data->items[field].spec = ip; > >> + vxlan_data->items[field].mask = &rte_flow_item_ipv4_mask; > >> + > >> + if (ip->ip_proto != IPPROTO_UDP) { > >> + goto err; > >> + } > >> + l4 = (ip + 1); > >> + } else if (eth->eth_type == htons(ETH_TYPE_IPV6)) { > >> + const struct ovs_16aligned_ip6_hdr *ip6 = l3; > >> + > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV6; > >> + vxlan_data->items[field].spec = ip6; > >> + vxlan_data->items[field].mask = &rte_flow_item_ipv6_mask; > >> + > >> + if (ip6->ip6_nxt != IPPROTO_UDP) { > >> + goto err; > >> + } > >> + l4 = (ip6 + 1); > >> + } else { > >> + goto err; > >> + } > >> + field++; > >> + > >> + udp = (const struct udp_header *)l4; > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_UDP; > >> + vxlan_data->items[field].spec = udp; > >> + vxlan_data->items[field].mask = &rte_flow_item_udp_mask; > >> + field++; > >> + > >> + vxlan = (udp + 1); > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_VXLAN; > >> + vxlan_data->items[field].spec = vxlan; > >> + vxlan_data->items[field].mask = &rte_flow_item_vxlan_mask; > >> + field++; > >> + > >> + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_END; > >> + > >> + vxlan_data->conf.definition = vxlan_data->items; > >> + > >> + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP, vxlan_data); > >> + > >> + return 0; > >> +err: > >> + free(vxlan_data); > >> + return -1; > >> +} > >> + > >> static int > >> parse_clone_actions(struct netdev *netdev, > >> struct flow_actions *actions, > >> @@ -1143,9 +1257,14 @@ parse_clone_actions(struct netdev *netdev, > >> > >> if (clone_type == OVS_ACTION_ATTR_TUNNEL_PUSH) { > >> const struct ovs_action_push_tnl *tnl_push = nl_attr_get(ca); > >> - struct rte_flow_action_raw_encap *raw_encap = > >> - xzalloc(sizeof *raw_encap); > >> + struct rte_flow_action_raw_encap *raw_encap; > >> + > >> + if (tnl_push->tnl_type == OVS_VPORT_TYPE_VXLAN && > >> + !add_vxlan_encap_action(actions, tnl_push->header)) { > >> + continue; > >> + } > >> > >> + raw_encap = xzalloc(sizeof *raw_encap); > >> raw_encap->data = (uint8_t *)tnl_push->header; > >> raw_encap->preserve = NULL; > >> raw_encap->size = tnl_push->header_len; > >> -- > >> 2.14.5 > >>
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c index 38f8f00ee..de313e40b 100644 --- a/lib/netdev-offload-dpdk.c +++ b/lib/netdev-offload-dpdk.c @@ -359,6 +359,25 @@ dump_flow_pattern(struct ds *s, const struct rte_flow_item *item) } else { ds_put_cstr(s, " Mask = null\n"); } + } else if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) { + const struct rte_flow_item_vxlan *vxlan_spec = item->spec; + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; + + ds_put_cstr(s, "rte flow vxlan pattern:\n"); + if (vxlan_spec) { + ds_put_format(s, " Spec: flags=0x%x, vni=%"PRIu32"\n", + vxlan_spec->flags, + ntohl(*(ovs_be32 *)vxlan_spec->vni) >> 8); + } else { + ds_put_cstr(s, " Spec = null\n"); + } + if (vxlan_mask) { + ds_put_format(s, " Mask: flags=0x%x, vni=0x%06"PRIx32"\n", + vxlan_mask->flags, + ntohl(*(ovs_be32 *)vxlan_mask->vni) >> 8); + } else { + ds_put_cstr(s, " Mask = null\n"); + } } else { ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type); } @@ -486,6 +505,14 @@ dump_flow_action(struct ds *s, const struct rte_flow_action *actions) } else { ds_put_cstr(s, " Raw-encap = null\n"); } + } else if (actions->type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { + const struct rte_flow_action_vxlan_encap *vxlan_encap = actions->conf; + const struct rte_flow_item *items = vxlan_encap->definition; + + ds_put_cstr(s, "rte flow vxlan-encap action:\n"); + while (items && items->type != RTE_FLOW_ITEM_TYPE_END) { + dump_flow_pattern(s, items++); + } } else { ds_put_format(s, "unknown rte flow action (%d)\n", actions->type); } @@ -1129,6 +1156,93 @@ parse_set_actions(struct flow_actions *actions, return 0; } +/* Maximum number of items in struct rte_flow_action_vxlan_encap. + * ETH / IPv4(6) / UDP / VXLAN / END + */ +#define ACTION_VXLAN_ENCAP_ITEMS_NUM 5 + +static int +add_vxlan_encap_action(struct flow_actions *actions, + const void *header) +{ + const struct eth_header *eth; + const struct udp_header *udp; + struct vxlan_data { + struct rte_flow_action_vxlan_encap conf; + struct rte_flow_item items[0]; + } *vxlan_data; + BUILD_ASSERT_DECL(offsetof(struct vxlan_data, conf) == 0); + const void *vxlan; + const void *l3; + const void *l4; + int field; + + vxlan_data = xzalloc(sizeof *vxlan_data + + sizeof(struct rte_flow_item) * + ACTION_VXLAN_ENCAP_ITEMS_NUM); + field = 0; + + eth = header; + /* Ethernet */ + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_ETH; + vxlan_data->items[field].spec = eth; + vxlan_data->items[field].mask = &rte_flow_item_eth_mask; + field++; + + l3 = eth + 1; + /* IP */ + if (eth->eth_type == htons(ETH_TYPE_IP)) { + /* IPv4 */ + const struct ip_header *ip = l3; + + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV4; + vxlan_data->items[field].spec = ip; + vxlan_data->items[field].mask = &rte_flow_item_ipv4_mask; + + if (ip->ip_proto != IPPROTO_UDP) { + goto err; + } + l4 = (ip + 1); + } else if (eth->eth_type == htons(ETH_TYPE_IPV6)) { + const struct ovs_16aligned_ip6_hdr *ip6 = l3; + + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_IPV6; + vxlan_data->items[field].spec = ip6; + vxlan_data->items[field].mask = &rte_flow_item_ipv6_mask; + + if (ip6->ip6_nxt != IPPROTO_UDP) { + goto err; + } + l4 = (ip6 + 1); + } else { + goto err; + } + field++; + + udp = (const struct udp_header *)l4; + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_UDP; + vxlan_data->items[field].spec = udp; + vxlan_data->items[field].mask = &rte_flow_item_udp_mask; + field++; + + vxlan = (udp + 1); + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_VXLAN; + vxlan_data->items[field].spec = vxlan; + vxlan_data->items[field].mask = &rte_flow_item_vxlan_mask; + field++; + + vxlan_data->items[field].type = RTE_FLOW_ITEM_TYPE_END; + + vxlan_data->conf.definition = vxlan_data->items; + + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP, vxlan_data); + + return 0; +err: + free(vxlan_data); + return -1; +} + static int parse_clone_actions(struct netdev *netdev, struct flow_actions *actions, @@ -1143,9 +1257,14 @@ parse_clone_actions(struct netdev *netdev, if (clone_type == OVS_ACTION_ATTR_TUNNEL_PUSH) { const struct ovs_action_push_tnl *tnl_push = nl_attr_get(ca); - struct rte_flow_action_raw_encap *raw_encap = - xzalloc(sizeof *raw_encap); + struct rte_flow_action_raw_encap *raw_encap; + + if (tnl_push->tnl_type == OVS_VPORT_TYPE_VXLAN && + !add_vxlan_encap_action(actions, tnl_push->header)) { + continue; + } + raw_encap = xzalloc(sizeof *raw_encap); raw_encap->data = (uint8_t *)tnl_push->header; raw_encap->preserve = NULL; raw_encap->size = tnl_push->header_len;