This patch covers the feature for converting the VxLAN-GPE NSH packet to Ethernet-NSH packet.
With this feature(options:nsh-convert=true),when VxLAN-GPE NSH packets (Outer MAC header + Outer IP header + UDP header + VxLAN-GPE + NSH + original packet) are received by VxLAN-GPE NSH port, the vport will remove Outer MAC header, Outer IP header, UDP header, VxLAN-GPE header, and then modify and push the outer MAC header. Then the packet with VxLAN-GPE+NSH format is converted to Outer MAC header + NSH header + original packet. Signed-off-by: Ricky Li <ricky...@intel.com> Signed-off-by: Mengke Liu <mengke....@intel.com> --- datapath/linux/compat/include/linux/openvswitch.h | 6 +- lib/netdev-vport.c | 109 +++++++++++++++++++++- lib/netdev.h | 6 ++ lib/odp-util.c | 80 ++++++++++++++-- lib/ovs-router.c | 64 +++++++++++++ lib/ovs-router.h | 1 + lib/packets.h | 13 ++- ofproto/ofproto-dpif-xlate.c | 64 ++++++++++++- ofproto/tunnel.c | 37 ++++++++ ofproto/tunnel.h | 5 + tests/tunnel.at | 32 +++++++ 11 files changed, 398 insertions(+), 19 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index b8ac152..3d588bb 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -230,6 +230,7 @@ enum ovs_vport_type { OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ + OVS_VPORT_TYPE_NSH, /* L2+NSH ENCAP tunnel. */ __OVS_VPORT_TYPE_MAX }; @@ -646,7 +647,10 @@ struct ovs_action_push_tnl { uint8_t header[TNL_PUSH_HEADER_SIZE]; }; -#define OVS_POP_SPEC_ACTION_NO_DECAP 2 +enum ovs_pop_spec_action_type { + OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH, + OVS_POP_SPEC_ACTION_NO_DECAP = 2, +}; /* * struct ovs_action_pop_tnl - %OVS_ACTION_ATTR_TUNNEL_POP_SPEC diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index d926c00..6e0d5ba 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -561,6 +561,23 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args) } else { tnl_cfg.ip_dst = in_addr.s_addr; } + } else if (!strcmp(node->key, "remote_mac")) { + if (!strcmp(node->value, "flow")) { + tnl_cfg.eth_dst_flow = true; + VLOG_ERR("remote_mac doesn't support setting by flow"); + return EINVAL; + } else if (eth_addr_from_string(node->value,&tnl_cfg.eth_dst)){ + tnl_cfg.eth_dst_present = true; + } else { + VLOG_WARN("%s: bad %s 'remote_mac'", name, type); + return EINVAL; + } + } else if (!strcmp(node->key, "nsh_convert")) { + if (!strcmp(node->value, "true")) { + tnl_cfg.nsh_convert = true; + } else { + tnl_cfg.nsh_convert = false; + } } else if (!strcmp(node->key, "tun_nodecap")) { if (!strcmp(node->value, "true")) { tnl_cfg.tun_nodecap = true; @@ -883,6 +900,13 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) if (!tnl_cfg.dont_fragment) { smap_add(args, "df_default", "false"); } + if (tnl_cfg.eth_dst_present) { + smap_add_format(args, "remote_mac", ETH_ADDR_FMT, ETH_ADDR_ARGS(tnl_cfg.eth_dst)); + } + + if (tnl_cfg.nsh_convert) { + smap_add(args, "nsh_convert", "true"); + } if (tnl_cfg.tun_nodecap) { smap_add(args, "tun_nodecap", "true"); @@ -1546,6 +1570,84 @@ netdev_vxlan_pop_header(struct dp_packet *packet) } static int +vxlan_extract_md_convert_to_eth_nsh(struct dp_packet *packet, const struct ovs_action_pop_tnl *data) +{ + struct pkt_metadata *md = &packet->md; + struct flow_tnl *tnl = &md->tunnel; + struct udp_header *udp; + + memset(md, 0, sizeof *md); + if (VXLAN_HLEN > dp_packet_size(packet)) { + return EINVAL; + } + + udp = ip_extract_tnl_md(packet, tnl); + if (!udp) { + return EINVAL; + } + + if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) { + + struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1); + + if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) { + VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n", + ntohl(get_16aligned_be32(&vxg->vx_vni))); + return EINVAL; + } + + tnl->tp_src = udp->udp_src; + tnl->tp_dst = udp->udp_dst; + tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8); + + if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) { + struct nshhdr *nsh = (struct nshhdr *) (vxg + 1); + struct eth_header *eth = NULL; + + tnl->nsp = nsh->b.b2 << 8; + tnl->nsi = nsh->b.svc_idx; + tnl->nshc1 = nsh->c.nshc1; + tnl->nshc2 = nsh->c.nshc2; + tnl->nshc3 = nsh->c.nshc3; + tnl->nshc4 = nsh->c.nshc4; + tnl->flags |= FLOW_TNL_F_NSP; + tnl->flags |= FLOW_TNL_F_NSI; + tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \ + FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4; + tnl->nsh_flags = NSH_TNL_F_ETHERNET; + + dp_packet_reset_packet(packet, VXNSH_HLEN - sizeof (struct nshhdr)); + eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len); + memcpy(eth, data->header, data->header_len); + eth->eth_type = htons(ETH_TYPE_NSH); + } else { + VLOG_WARN("Unsupported vxlan GPE + NSH format!"); + return EINVAL; + } + + } else { + + struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1); + + if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) || + (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) { + VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n", + ntohl(get_16aligned_be32(&vxh->vx_flags)), + ntohl(get_16aligned_be32(&vxh->vx_vni))); + return EINVAL; + } + + tnl->tp_src = udp->udp_src; + tnl->tp_dst = udp->udp_dst; + tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8); + dp_packet_reset_packet(packet, VXLAN_HLEN); + } + + return 0; + +} + +static int vxlan_extract_md_no_decap(struct dp_packet *packet) { struct pkt_metadata *md = &packet->md; @@ -1595,6 +1697,7 @@ vxlan_extract_md_no_decap(struct dp_packet *packet) tnl->flags |= FLOW_TNL_F_NSI; tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4; + tnl->tun_len = VXNSH_HLEN; tnl->nsh_flags = NSH_TNL_F_NODECAP; } else { VLOG_WARN("Unsupported vxlan GPE + NSH format!"); @@ -1606,19 +1709,19 @@ vxlan_extract_md_no_decap(struct dp_packet *packet) return 0; } - static int netdev_vxlan_pop_header_spec(struct dp_packet *packet, const struct ovs_action_pop_tnl *data) { - if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) { + if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) { + return vxlan_extract_md_convert_to_eth_nsh(packet, data); + } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) { return vxlan_extract_md_no_decap(packet); } return EINVAL; } - static int netdev_vxlan_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, diff --git a/lib/netdev.h b/lib/netdev.h index b30c932..26013ef 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -150,6 +150,10 @@ struct netdev_tunnel_config { bool ipsec; bool dont_fragment; + bool eth_dst_present; + bool eth_dst_flow; + struct eth_addr eth_dst; + bool in_nshc1_present; bool in_nshc1_flow; ovs_be32 in_nshc1; /* incoming NSH context c1 */ @@ -182,6 +186,7 @@ struct netdev_tunnel_config { bool out_nshc4_flow; ovs_be32 out_nshc4; /* outgoing NSH context c4 */ + bool nsh_convert; bool tun_nodecap; }; @@ -247,6 +252,7 @@ int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int netdev_pop_header_spec(struct netdev *netdev, struct dp_packet **buffers, int cnt, const struct ovs_action_pop_tnl *data); + /* Hardware address. */ int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac); int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac); diff --git a/lib/odp-util.c b/lib/odp-util.c index 190117f..6da2d5b 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -552,16 +552,22 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data) } static void -format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr) +format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data) { - struct ovs_action_pop_tnl *data; + const struct eth_header *eth; - data = (struct ovs_action_pop_tnl *) nl_attr_get(attr); + eth = (const struct eth_header *)data->header; + if (data->tnl_type == OVS_VPORT_TYPE_NSH) { + const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1); - ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port); - if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) { - ds_put_format(ds, "pop_type=%"PRIu16")", - OVS_POP_SPEC_ACTION_NO_DECAP); + /* Ethernet */ + ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=", + data->header_len, data->tnl_type); + ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst)); + ds_put_format(ds, ",src="); + ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src)); + ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type)); + ds_put_format(ds, "),"); } } @@ -578,6 +584,26 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr) } static void +format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr) +{ + struct ovs_action_pop_tnl *data; + + data = (struct ovs_action_pop_tnl *) nl_attr_get(attr); + + ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port); + if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) { + ds_put_format(ds, "pop_type=%"PRIu16",", + OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH); + format_odp_tnl_pop_header(ds, data); + ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port); + + } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) { + ds_put_format(ds, "pop_type=%"PRIu16")", + OVS_POP_SPEC_ACTION_NO_DECAP); + } +} + +static void format_odp_action(struct ds *ds, const struct nlattr *a) { int expected_len; @@ -1050,11 +1076,8 @@ static int ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data) { struct eth_header *eth; - struct nshhdr *nsh; uint32_t tnl_type = 0, header_len = 0; uint16_t dl_type; - ovs_be32 nsp, nshc1, nshc2, nshc3, nshc4; - uint8_t nsi; int n = 0; if (!ovs_scan_len(s, &n, "tnl_pop_spec(tnl_port(%"SCNi32"),", &data->tnl_port)) { @@ -1068,6 +1091,42 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data) if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) { return n; + + } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) { + + eth = (struct eth_header *) data->header; + + if (!ovs_scan_len(s, &n, ",header(size=%"SCNi32",type=%"SCNi32"," + "eth(dst="ETH_ADDR_SCAN_FMT",", + &data->header_len, + &data->tnl_type, + ETH_ADDR_SCAN_ARGS(eth->eth_dst))) { + return -EINVAL; + } + if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",", + ETH_ADDR_SCAN_ARGS(eth->eth_src))) { + return -EINVAL; + } + if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) { + return -EINVAL; + } + eth->eth_type = htons(dl_type); + + tnl_type = OVS_VPORT_TYPE_NSH; + header_len = sizeof *eth; + + /* check tunnel meta data. */ + if (data->tnl_type != tnl_type) { + return -EINVAL; + } + if (data->header_len != header_len) { + return -EINVAL; + } + + /* Out port */ + if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) { + return -EINVAL; + } } else { return -EINVAL; } @@ -1075,6 +1134,7 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data) return n; } + static int parse_odp_action(const char *s, const struct simap *port_names, struct ofpbuf *actions) diff --git a/lib/ovs-router.c b/lib/ovs-router.c index d6c7652..9f61bac 100644 --- a/lib/ovs-router.c +++ b/lib/ovs-router.c @@ -82,6 +82,24 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) return route_table_fallback_lookup(ip_dst, output_bridge, gw); } +bool +ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[]) +{ + const struct cls_rule *cr; + struct flow s_flow; + + memset(&s_flow, 0, sizeof (struct flow)); + memcpy(s_flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN); + cr = classifier_lookup(&cls,CLS_MAX_VERSION, &s_flow, NULL); + if (cr) { + struct ovs_router_entry *p = ovs_router_entry_cast(cr); + + strncpy(output_bridge, p->output_bridge, IFNAMSIZ); + return true; + } + return false; +} + static void rt_entry_free(struct ovs_router_entry *p) { @@ -133,6 +151,36 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen, seq_change(tnl_conf_seq); } +static void +ovs_router_insert_mac__(uint8_t priority, struct eth_addr *mac, + const char output_bridge[]) +{ + const struct cls_rule *cr; + struct ovs_router_entry *p; + struct match s_match; + + memset(&s_match, 0, sizeof (struct match)); + memcpy(s_match.flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN); + + p = xzalloc(sizeof *p); + strncpy(p->output_bridge, output_bridge, IFNAMSIZ); + p->gw = 0; + p->nw_addr = 0; + p->plen = 32; + p->priority = priority; + cls_rule_init(&p->cr, &s_match, priority); /* Longest prefix matches first. */ + + ovs_mutex_lock(&mutex); + cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0); + ovs_mutex_unlock(&mutex); + + if (cr) { + /* An old rule with the same match was displaced. */ + ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); + } + seq_change(tnl_conf_seq); +} + void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[], ovs_be32 gw) @@ -231,6 +279,20 @@ ovs_router_add(struct unixctl_conn *conn, int argc, } static void +ovs_router_add_mac(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) +{ + struct eth_addr mac; + + if (eth_addr_from_string(argv[1], &mac)) { + ovs_router_insert_mac__(48, &mac, argv[2]); + unixctl_command_reply(conn, "OK"); + } else { + unixctl_command_reply(conn, "Invalid parameters"); + } +} + +static void ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { @@ -326,6 +388,8 @@ ovs_router_init(void) classifier_init(&cls, NULL); unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3, ovs_router_add, NULL); + unixctl_command_register("ovs/route/addmac", "mac_addr out_br_name", 2, 2, + ovs_router_add_mac, NULL); unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL); unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del, NULL); diff --git a/lib/ovs-router.h b/lib/ovs-router.h index cc0ebc2..3f5a504 100644 --- a/lib/ovs-router.h +++ b/lib/ovs-router.h @@ -23,6 +23,7 @@ extern "C" { #endif +bool ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[]); bool ovs_router_lookup(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw); void ovs_router_init(void); void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, diff --git a/lib/packets.h b/lib/packets.h index 87c955a..c586390 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -33,6 +33,8 @@ struct dp_packet; struct ds; +#define ETH_ADDR_LEN 6 + /* Tunnel information used in flow key and metadata. */ struct flow_tnl { ovs_be32 ip_dst; @@ -52,7 +54,9 @@ struct flow_tnl { ovs_be32 nshc2; ovs_be32 nshc3; ovs_be32 nshc4; - uint8_t pad1[7]; /* Pad to 64 bits. */ + struct eth_addr eth_dst; + uint8_t tun_len; + uint8_t pad1[4]; /* Pad to 64 bits. */ struct tun_metadata metadata; }; @@ -83,7 +87,9 @@ struct flow_tnl { #define FLOW_TNL_F_NSH_C3 (1 << 9) #define FLOW_TNL_F_NSH_C4 (1 << 10) -#define NSH_TNL_F_NODECAP (1 << 1) +#define NSH_TNL_F_ETHERNET (1 << 0) +#define NSH_TNL_F_VXLAN (1 << 1) +#define NSH_TNL_F_NODECAP (1 << 2) /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */ static inline size_t @@ -160,8 +166,6 @@ pkt_metadata_init(struct pkt_metadata *md, odp_port_t port) bool dpid_from_string(const char *s, uint64_t *dpidp); -#define ETH_ADDR_LEN 6 - static const struct eth_addr eth_addr_broadcast OVS_UNUSED = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; @@ -352,6 +356,7 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, #define ETH_TYPE_RARP 0x8035 #define ETH_TYPE_MPLS 0x8847 #define ETH_TYPE_MPLS_MCAST 0x8848 +#define ETH_TYPE_NSH 0x894f static inline bool eth_type_mpls(ovs_be16 eth_type) { diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 71e255e..bff0a83 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -2690,6 +2690,36 @@ tnl_route_lookup_flow(const struct flow *oflow, } static int +tnl_outdev_lookup_mac(const struct eth_addr *mac, + struct xport **out_port) +{ + char out_dev[IFNAMSIZ]; + struct xbridge *xbridge; + struct xlate_cfg *xcfg; + + if (!ovs_router_lookup_mac(mac, out_dev)) { + return -ENOENT; + } + + xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); + ovs_assert(xcfg); + + HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) { + if (!strncmp(xbridge->name, out_dev, IFNAMSIZ)) { + struct xport *port; + + HMAP_FOR_EACH (port, ofp_node, &xbridge->xports) { + if (!strncmp(netdev_get_name(port->netdev), out_dev, IFNAMSIZ)) { + *out_port = port; + return 0; + } + } + } + } + return -ENOENT; +} + +static int compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev, struct dp_packet *packet) { @@ -2795,7 +2825,39 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct cfg = tnl_port_cfg(tunnel_odp_port, flow); if (cfg) { - if (cfg->tun_nodecap) { + if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) { + struct ovs_action_pop_tnl tnl_pop_data; + struct xport *out_dev = NULL; + struct eth_addr smac; + + int err; + + err = tnl_outdev_lookup_mac(&cfg->eth_dst, &out_dev); + if (err) { + VLOG_WARN("tnl_outdev_lookup_mac failed..."); + return err; + } + + /* Use mac addr of bridge port of the peer. */ + err = netdev_get_etheraddr(out_dev->netdev, &smac); + if (err) { + VLOG_WARN("netdev_get_etheraddr failed..."); + return err; + } + + err = tnl_port_build_nsh_header_odport_popspec(tunnel_odp_port, flow, + &cfg->eth_dst, &smac, &tnl_pop_data); + if (err) { + VLOG_WARN("tnl_port_build_nsh_header failed..."); + return err; + } + tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port); + tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port); + tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH; + tnl_pop_data.tnl_type = OVS_VPORT_TYPE_NSH; + odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data); + + } else if (cfg->tun_nodecap) { struct ovs_action_pop_tnl tnl_pop_data; memset(&tnl_pop_data, 0, sizeof tnl_pop_data); diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index 4606fb6..b0e46e6 100644 --- a/ofproto/tunnel.c +++ b/ofproto/tunnel.c @@ -45,6 +45,8 @@ VLOG_DEFINE_THIS_MODULE(tunnel); /* skb mark used for IPsec tunnel packets */ #define IPSEC_MARK 1 +#define ETH_NSH_HLEN (sizeof(struct eth_header) + \ + sizeof(struct nshhdr)) struct tnl_match { ovs_be64 in_key; ovs_be32 in_nsp; @@ -568,6 +570,9 @@ tnl_port_cfg(odp_port_t odp_port, struct flow *flow) OVS_EXCLUDED(rwlock) cfg = netdev_get_tunnel_config(tnl_port->netdev); ovs_assert(cfg); + if (!cfg->eth_dst_flow) { + memcpy(flow->tunnel.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN); + } if (!cfg->out_nsp_flow) { flow->tunnel.nsp = cfg->out_nsp; } @@ -602,6 +607,7 @@ out: return cfg; } + static uint32_t tnl_hash(struct tnl_match *match) { @@ -1063,3 +1069,34 @@ tnl_port_build_header(const struct ofport_dpif *ofport, return res; } + +int +tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port, + const struct flow *tnl_flow OVS_UNUSED, + const struct eth_addr *dmac, + const struct eth_addr *smac, + struct ovs_action_pop_tnl *data) +{ + struct tnl_port *tnl_port; + struct eth_header *eth; + int res = 0; + + fat_rwlock_rdlock(&rwlock); + tnl_port = tnl_find_odp_port(odp_port); + ovs_assert(tnl_port); + + /* Build Ethernet and IP headers. */ + memset(data->header, 0, sizeof data->header); + + eth = (struct eth_header *)data->header; + memcpy(eth->eth_dst.ea, dmac->ea, ETH_ADDR_LEN); + memcpy(eth->eth_src.ea, smac->ea, ETH_ADDR_LEN); + eth->eth_type = htons(ETH_TYPE_NSH); + + data->header_len = ETH_NSH_HLEN - sizeof (struct nshhdr); + data->tnl_type = OVS_VPORT_TYPE_NSH; + + fat_rwlock_unlock(&rwlock); + + return res; +} diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h index 2b608ce..0c51a4e 100644 --- a/ofproto/tunnel.h +++ b/ofproto/tunnel.h @@ -59,4 +59,9 @@ int tnl_port_build_header(const struct ofport_dpif *ofport, const struct eth_addr dmac, const struct eth_addr smac, ovs_be32 ip_src, struct ovs_action_push_tnl *data); +int tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port, + const struct flow *tnl_flow OVS_UNUSED, + const struct eth_addr *dmac, + const struct eth_addr *smac, + struct ovs_action_pop_tnl *data); #endif /* tunnel.h */ diff --git a/tests/tunnel.at b/tests/tunnel.at index 851afdc..dc35809 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -673,6 +673,38 @@ AT_CHECK([tail -1 stdout], [0], OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"]) AT_CLEANUP +AT_SETUP([tunnel - VXLAN-GPE NSH - nsh_convert from VXLAN-GPE NSH to Ethernet NSH - user space]) +OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) +AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=vxlan options:remote_ip=1.1.1.1 options:dst_port=4790 \ + options:nsh_convert=true options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:remote_mac=00:00:00:11:11:22 ofport_request=2]) +AT_CHECK([ovs-vsctl add-port int-br p2 -- set Interface p2 type=dummy ofport_request=3]) + +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/addmac 00:00:00:11:11:22 br0],[0],[dnl +OK +]) + +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) + +AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl +Listening ports: +vxlan_sys_4790 (4790) +]) + +dnl remote_ip p0 +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=4790)'], [0], [stdout]) + +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=0,header(size=14,type=107,eth(dst=00:00:00:11:11:22,src=aa:55:aa:55:00:00,dl_type=0x894f)),out_port(100)) +]) + +OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"]) +AT_CLEANUP + AT_SETUP([tunnel - Geneve metadata]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- 1.9.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev