This is useful for generating an ARP request from the OpenFlow flow table, which in turn is useful for implementing a router. An upcoming commit will use this feature to implement ARP request generationg in the OVN L3 logical router.
Signed-off-by: Ben Pfaff <b...@ovn.org> --- NEWS | 2 + datapath/linux/compat/include/linux/openvswitch.h | 5 +- lib/dpif-netdev.c | 3 +- lib/dpif.c | 1 + lib/odp-execute.c | 26 +++++ lib/odp-util.c | 12 ++ lib/ofp-actions.c | 129 +++++++++++++++++++++- lib/ofp-actions.h | 3 +- lib/ofp-errors.h | 4 + lib/packets.c | 36 +++--- lib/packets.h | 1 + ofproto/ofproto-dpif-sflow.c | 4 + ofproto/ofproto-dpif-xlate.c | 70 ++++++++++++ tests/ofproto-dpif.at | 65 +++++++++++ utilities/ovs-ofctl.8.in | 26 +++++ 15 files changed, 370 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index a6b1599..578171d 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ Post-v2.5.0 --------------------- + - OpenFlow: + * New "arp" OpenFlow extension action for generating ARP packets. v2.5.0 - xx xxx xxxx diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 3b39ebb..5acf539 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2014 Nicira, Inc. + * Copyright (c) 2007-2015 Nicira, Inc. * * This file is offered under your choice of two licenses: Apache 2.0 or GNU * GPL 2.0 or later. The permission statements for each of these licenses is @@ -783,6 +783,8 @@ enum ovs_nat_attr { * ovs_action_push_tnl. * @OVS_ACTION_ATTR_TUNNEL_POP: Lookup tunnel port by port-no passed and pop * tunnel header. + * @OVS_ACTION_ATTR_ARP: Transform IPv4 packet into ARP packet, execute nested + * actions, restore IPv4 packet. */ enum ovs_action_attr { @@ -806,6 +808,7 @@ enum ovs_action_attr { #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */ + OVS_ACTION_ATTR_ARP, /* Nested OVS_ACTION_ATTR_*. */ #endif __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index a67ef05..d382fde 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -3627,6 +3627,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_HASH: + case OVS_ACTION_ATTR_ARP: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/dpif.c b/lib/dpif.c index 38e40ba..2926668 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1146,6 +1146,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: + case OVS_ACTION_ATTR_ARP: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/odp-execute.c b/lib/odp-execute.c index b5204b2..5e5c3ca 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -34,6 +34,8 @@ #include "unaligned.h" #include "util.h" +#include "openvswitch/vlog.h" + /* Masked copy of an ethernet address. 'src' is already properly masked. */ static void ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src, @@ -480,6 +482,23 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal, nl_attr_get_size(subactions), dp_execute_action); } +static void +odp_execute_arp(void *dp, struct dp_packet *ip, + const struct nlattr *action, odp_execute_cb dp_execute_action) +{ + uint64_t stub[DIV_ROUND_UP(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + + ARP_ETH_HEADER_LEN, 8)]; + struct dp_packet arp; + dp_packet_use_stub(&arp, stub, sizeof stub); + compose_arp__(&arp); + arp.md = ip->md; + + struct dp_packet *arpp = &arp; + odp_execute_actions(dp, &arpp, 1, false, nl_attr_get(action), + nl_attr_get_size(action), dp_execute_action); + dp_packet_uninit(&arp); +} + static bool requires_datapath_assistance(const struct nlattr *a) { @@ -503,6 +522,7 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: + case OVS_ACTION_ATTR_ARP: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -623,6 +643,12 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal, } break; + case OVS_ACTION_ATTR_ARP: + for (i = 0; i < cnt; i++) { + odp_execute_arp(dp, packets[i], a, dp_execute_action); + } + break; + case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: diff --git a/lib/odp-util.c b/lib/odp-util.c index 90942c7..8fe14c5 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -120,6 +120,7 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE; + case OVS_ACTION_ATTR_ARP: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -766,6 +767,14 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr) } static void +format_odp_arp_action(struct ds *ds, const struct nlattr *attr) +{ + ds_put_cstr(ds, "arp("); + format_odp_actions(ds, nl_attr_get(attr), nl_attr_get_size(attr)); + ds_put_char(ds, ')'); +} + +static void format_odp_action(struct ds *ds, const struct nlattr *a) { int expected_len; @@ -858,6 +867,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_CT: format_odp_conntrack_action(ds, a); break; + case OVS_ACTION_ATTR_ARP: + format_odp_arp_action(ds, a); + break; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 354c768..92b296b 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -295,6 +295,9 @@ enum ofp_raw_action_type { /* NX1.0+(36): struct nx_action_nat, ... */ NXAST_RAW_NAT, + /* NX1.0+(37): struct nx_action_arp, ... */ + NXAST_RAW_ARP, + /* ## ------------------ ## */ /* ## Debugging actions. ## */ /* ## ------------------ ## */ @@ -423,6 +426,9 @@ ofpact_next_flattened(const struct ofpact *ofpact) case OFPACT_CT: return ofpact_get_CT(ofpact)->actions; + case OFPACT_ARP: + return ofpact_get_ARP(ofpact)->actions; + case OFPACT_WRITE_ACTIONS: return ofpact_get_WRITE_ACTIONS(ofpact)->actions; } @@ -5349,7 +5355,108 @@ parse_NAT(char *arg, struct ofpbuf *ofpacts, } return NULL; } + +/* Action structure for NXAST_ARP. + * + * The only content is a series of nested OpenFlow actions to act on the ARP + * packet. + */ +struct nx_action_arp { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_ARP. */ + uint8_t zero[6]; /* Must be zero. */ + /* Followed by a sequence of zero or more OpenFlow actions. The length of + * these is included in 'len'. */ +}; +OFP_ASSERT(sizeof(struct nx_action_arp) == 16); + +static enum ofperr +decode_NXAST_RAW_ARP(const struct nx_action_arp *naa, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + const size_t arp_offset = ofpacts_pull(out); + struct ofpact_nest *arp; + struct ofpbuf openflow; + int error = 0; + + if (!is_all_zeros(naa->zero, sizeof naa->zero)) { + return OFPERR_NXBAC_MUST_BE_ZERO; + } + + arp = ofpact_put_ARP(out); + ofpbuf_pull(out, sizeof *arp); + + ofpbuf_use_const(&openflow, naa + 1, ntohs(naa->len) - sizeof *naa); + error = ofpacts_pull_openflow_actions__( + &openflow, openflow.size, ofp_version, + 1u << OVSINST_OFPIT11_APPLY_ACTIONS, out, OFPACT_ARP); + if (error) { + goto out; + } + + arp = ofpbuf_push_uninit(out, sizeof *arp); + out->header = &arp->ofpact; + ofpact_update_len(out, &arp->ofpact); + +out: + ofpbuf_push_uninit(out, arp_offset); + return error; +} + +static void +encode_ARP(const struct ofpact_nest *arp, enum ofp_version ofp_version, + struct ofpbuf *out) +{ + struct nx_action_arp *naa; + const size_t ofs = out->size; + size_t len; + + naa = put_NXAST_ARP(out); + + len = ofpacts_put_openflow_actions(arp->actions, + ofpact_nest_get_action_len(arp), + out, ofp_version); + len += sizeof *naa; + naa = ofpbuf_at(out, ofs, sizeof *naa); + naa->len = htons(len); +} + +/* Parses 'arg' as the argument to a "arp" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * OVS_WARN_UNUSED_RESULT +parse_ARP(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + const size_t arp_offset = ofpacts_pull(ofpacts); + struct ofpact_nest *arp = ofpact_put_ARP(ofpacts); + + /* Hide existing actions from ofpacts_parse_copy(), so the + * nesting can be handled transparently. */ + ofpbuf_pull(ofpacts, sizeof *arp); + char *error = ofpacts_parse_copy(arg, ofpacts, usable_protocols, false, + OFPACT_ARP); + ofpact_pad(ofpacts); + ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof *arp); + arp = ofpacts->header; + + ofpact_update_len(ofpacts, &arp->ofpact); + ofpbuf_push_uninit(ofpacts, arp_offset); + + return error; +} +static void +format_ARP(const struct ofpact_nest *a, struct ds *s) +{ + ds_put_cstr(s, "arp("); + ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s); + ds_put_char(s, ')'); +} /* Meter instruction. */ @@ -5749,6 +5856,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a) case OFPACT_OUTPUT: case OFPACT_OUTPUT_REG: case OFPACT_POP_MPLS: + case OFPACT_ARP: case OFPACT_POP_QUEUE: case OFPACT_PUSH_MPLS: case OFPACT_PUSH_VLAN: @@ -5822,6 +5930,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) case OFPACT_SAMPLE: case OFPACT_STACK_POP: case OFPACT_STACK_PUSH: + case OFPACT_ARP: case OFPACT_DEBUG_RECIRC: /* The action set may only include actions and thus @@ -6024,6 +6133,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_DEC_MPLS_TTL: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: + case OFPACT_ARP: case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: @@ -6592,6 +6702,21 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype; return 0; + case OFPACT_ARP: { + if (flow->dl_type != htons(ETH_TYPE_IP)) { + return OFPERR_NXBAC_ARP_REQUIRES_IPV4; + } + + flow->dl_type = htons(ETH_TYPE_ARP); + struct ofpact_nest *oa = ofpact_get_ARP(a); + enum ofperr error = ofpacts_check(oa->actions, + ofpact_nest_get_action_len(oa), + flow, max_ports, table_id, n_tables, + usable_protocols); + flow->dl_type = htons(ETH_TYPE_IP); + return error; + } + case OFPACT_SAMPLE: return 0; @@ -6784,7 +6909,8 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action) if (outer_action) { ovs_assert(outer_action == OFPACT_WRITE_ACTIONS - || outer_action == OFPACT_CT); + || outer_action == OFPACT_CT + || outer_action == OFPACT_ARP); if (outer_action == OFPACT_CT) { if (!field) { @@ -7130,6 +7256,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: + case OFPACT_ARP: case OFPACT_SET_TUNNEL: case OFPACT_WRITE_METADATA: case OFPACT_SET_QUEUE: diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 18c7395..66b82ed 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -86,6 +86,7 @@ OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact, "dec_mpls_ttl") \ OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \ OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \ + OFPACT(ARP, ofpact_nest, ofpact, "arp") \ \ /* Metadata. */ \ OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \ @@ -475,7 +476,7 @@ struct ofpact_meter { /* OFPACT_WRITE_ACTIONS. * - * Used for OFPIT11_WRITE_ACTIONS. */ + * Used for OFPIT11_WRITE_ACTIONS and NXAST_ARP. */ struct ofpact_nest { struct ofpact ofpact; uint8_t pad[PAD_SIZE(sizeof(struct ofpact), OFPACT_ALIGNTO)]; diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h index 9c108d0..f40faf7 100644 --- a/lib/ofp-errors.h +++ b/lib/ofp-errors.h @@ -269,6 +269,10 @@ enum ofperr { * 64. */ OFPERR_NXBAC_BAD_CONJUNCTION, + /* NX1.0-1.1(2,527), NX1.2+(23). ARP is allowed only in a flow that + * matches on Ethertype 0x800. */ + OFPERR_NXBAC_ARP_REQUIRES_IPV4, + /* ## --------------------- ## */ /* ## OFPET_BAD_INSTRUCTION ## */ /* ## --------------------- ## */ diff --git a/lib/packets.c b/lib/packets.c index d82341d..386a6a4 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -1191,35 +1191,45 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags) * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination * 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if - * 'broadcast' is true. */ + * 'broadcast' is true. Points the L3 header to the ARP header. */ void compose_arp(struct dp_packet *b, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa) { - struct eth_header *eth; - struct arp_eth_header *arp; + compose_arp__(b); + + struct eth_header *eth = dp_packet_l2(b); + eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha; + eth->eth_src = arp_sha; + + struct arp_eth_header *arp = dp_packet_l3(b); + arp->ar_op = htons(arp_op); + arp->ar_sha = arp_sha; + arp->ar_tha = arp_tha; + put_16aligned_be32(&arp->ar_spa, arp_spa); + put_16aligned_be32(&arp->ar_tpa, arp_tpa); +} +/* Clears 'b' and replaces its contents by an ARP frame. Sets the fields in + * the Ethernet and ARP headers that are fixed for ARP frames to those fixed + * values, and zeroes the other fields. Points the L3 header to the ARP + * header. */ +void +compose_arp__(struct dp_packet *b) +{ dp_packet_clear(b); dp_packet_prealloc_tailroom(b, ARP_PACKET_SIZE); dp_packet_reserve(b, 2 + VLAN_HEADER_LEN); - eth = dp_packet_put_uninit(b, sizeof *eth); - eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha; - eth->eth_src = arp_sha; + struct eth_header *eth = dp_packet_put_zeros(b, sizeof *eth); eth->eth_type = htons(ETH_TYPE_ARP); - arp = dp_packet_put_uninit(b, sizeof *arp); + struct arp_eth_header *arp = dp_packet_put_zeros(b, sizeof *arp); arp->ar_hrd = htons(ARP_HRD_ETHERNET); arp->ar_pro = htons(ARP_PRO_IP); arp->ar_hln = sizeof arp->ar_sha; arp->ar_pln = sizeof arp->ar_spa; - arp->ar_op = htons(arp_op); - arp->ar_sha = arp_sha; - arp->ar_tha = arp_tha; - - put_16aligned_be32(&arp->ar_spa, arp_spa); - put_16aligned_be32(&arp->ar_tpa, arp_tpa); dp_packet_reset_offsets(b); dp_packet_set_l3(b, arp); diff --git a/lib/packets.h b/lib/packets.h index eaa329f..cde0743 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1041,6 +1041,7 @@ void packet_set_nd(struct dp_packet *, const ovs_be32 target[4], void packet_format_tcp_flags(struct ds *, uint16_t); const char *packet_tcp_flag_to_string(uint32_t flag); +void compose_arp__(struct dp_packet *); void compose_arp(struct dp_packet *, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index f11699c..4db3357 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1138,6 +1138,10 @@ dpif_sflow_read_actions(const struct flow *flow, } break; + case OVS_ACTION_ATTR_ARP: + /* XXX Do not handle this for now. */ + break; + case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_HASH: diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index cf184e4..c1ae417 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -4163,6 +4163,7 @@ recirc_unroll_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_CONTROLLER: case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: + case OFPACT_ARP: recirc_put_unroll_xlate(ctx); break; @@ -4387,6 +4388,71 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc) } static void +execute_arp_action(struct xlate_ctx *ctx, const struct ofpact_nest *arp) +{ + ctx->xout->slow |= SLOW_ACTION; + + /* Commit the current flow and save it. + * + * (We don't save ctx->base_flow separately because the commit should + * ensure that it is identical.) */ + xlate_commit_actions(ctx); + struct flow old_flow = ctx->xin->flow; + + /* Transform ctx->in->flow to ARP. */ + ctx->xin->flow.dl_type = htons(ETH_TYPE_ARP); + ctx->xin->flow.nw_proto = ARP_OP_REQUEST; + ctx->xin->flow.arp_sha = ctx->xin->flow.dl_src; + ctx->xin->flow.arp_tha = ctx->xin->flow.dl_dst; + + /* This introduces a read dependency on dl_src and dl_dst, so mark them in + * the wildcard mask. We also have to mark the ARP fields; otherwise at + * commit time we won't even consider them. */ + memset(&ctx->wc->masks.dl_src, 0xff, sizeof ctx->wc->masks.dl_src); + memset(&ctx->wc->masks.dl_dst, 0xff, sizeof ctx->wc->masks.dl_dst); + memset(&ctx->wc->masks.nw_src, 0xff, sizeof ctx->wc->masks.nw_src); + memset(&ctx->wc->masks.nw_dst, 0xff, sizeof ctx->wc->masks.nw_dst); + memset(&ctx->wc->masks.nw_proto, 0xff, sizeof ctx->wc->masks.nw_proto); + memset(&ctx->wc->masks.arp_sha, 0xff, sizeof ctx->wc->masks.arp_sha); + memset(&ctx->wc->masks.arp_tha, 0xff, sizeof ctx->wc->masks.arp_tha); + + /* Transform ctx->base_flow to ARP. This differs from ctx->xin->flow + * because the datapath ARP action zeroes all field values. */ + ctx->base_flow = ctx->xin->flow; + ctx->base_flow.vlan_tci = 0; + ctx->base_flow.dl_src = eth_addr_zero; + ctx->base_flow.dl_dst = eth_addr_zero; + ctx->base_flow.nw_src = 0; /* arp_spa */ + ctx->base_flow.nw_dst = 0; /* arp_tpa */ + ctx->base_flow.arp_sha = eth_addr_zero; + ctx->base_flow.arp_tha = eth_addr_zero; + + /* Save ctx->xin->packet. Then, if it was nonnull, replace it by a + * synthesized ARP packet. */ + const struct dp_packet *old_packet = ctx->xin->packet; + uint64_t stub[DIV_ROUND_UP(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + + ARP_ETH_HEADER_LEN, 8)]; + struct dp_packet packet; + if (old_packet) { + dp_packet_use_stub(&packet, stub, sizeof stub); + compose_arp__(&packet); + packet.md = old_packet->md; + ctx->xin->packet = &packet; + } + + size_t start = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_ARP); + do_xlate_actions(arp->actions, ofpact_nest_get_action_len(arp), ctx); + nl_msg_end_nested(ctx->odp_actions, start); + + /* Restore old flow and base_flow. Restore old packet. */ + ctx->base_flow = ctx->xin->flow = old_flow; + if (old_packet) { + dp_packet_uninit(&packet); + ctx->xin->packet = old_packet; + } +} + +static void do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct xlate_ctx *ctx) { @@ -4634,6 +4700,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype); break; + case OFPACT_ARP: + execute_arp_action(ctx, ofpact_get_ARP(a)); + break; + case OFPACT_SET_MPLS_LABEL: CHECK_MPLS_RECIRCULATION(); compose_set_mpls_label_action( diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 60734db..73f7641 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -3044,6 +3044,71 @@ arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:f OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto-dpif - IP to ARP transformation slow-path]) +OVS_VSWITCHD_START + +# Add pairs of ports for input and output. +ADD_OF_PORTS([br0], [1], [2], [3], [4], [11], [12], [13], [14]) +for i in 11 12 13 14; do + ovs-vsctl -- set Interface p$i type=dummy options:pcap=p$i.pcap +done + +# Add flows that do various kind of "arp" action transformations +# from port 1 to 11, 2 to 12, 3 to 13, 4 to 14. +AT_DATA([flows.txt], [dnl +in_port=1,ip actions=arp(11),arp(set_field:2->arp_op,11),11 +in_port=2,ip actions=mod_vlan_vid:123,arp(12),arp(set_field:2->arp_op,12),12 +in_port=3,ip actions=arp(mod_vlan_vid:123,13),arp(set_field:2->arp_op,13),13 +in_port=4,ip actions=arp(14),strip_vlan,arp(set_field:2->arp_op,14),14 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +# Input two packets (one without a VLAN, one with VLAN 99) on each input port. +for i in 1 2 3 4; do + ovs-appctl netdev-dummy/receive p$i "in_port($i),eth(src=80:88:88:88:88:88,dst=10:20:30:40:50:60),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=10)" + ovs-appctl netdev-dummy/receive p$i "in_port($i),eth(src=80:88:88:88:88:88,dst=10:20:30:40:50:60),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=10))" +done +AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) + +# Check the packets that were output. +AT_CHECK([ovs-ofctl parse-pcap p11.pcap], [0], [dnl +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +]) + +AT_CHECK([ovs-ofctl parse-pcap p12.pcap], [0], [dnl +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +]) + +AT_CHECK([ovs-ofctl parse-pcap p13.pcap], [0], [dnl +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +]) + +AT_CHECK([ovs-ofctl parse-pcap p14.pcap], [0], [dnl +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60 +udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto-dpif - VLAN handling]) OVS_VSWITCHD_START( [set Bridge br0 fail-mode=standalone -- \ diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index c37ced3..dc704b6 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1740,6 +1740,32 @@ nf_conntrack module loaded. . .RE . +.IP \fBarp(\fIaction\fR...\fB)\fR +Transforms an IPv4 packet into an ARP packet, then executes each +nested \fIaction\fR on the ARP packet. Actions following the +\fBarp\fR action apply to the original packet, not the ARP packet, +discarding any changes that the nested actions made to packet data or +metadata. +. +.IP +The initial values of fields in the ARP packet are derived as follows: +.RS +.IP "\fBdl_src\fR and \fBdl_dst\fR" +Unchanged from the IPv4 packet. +.IP "\fBdl_type\fR" +0x0806 (ARP) +.IP "\fBnw_proto\fR (ARP opcode)" +1 (ARP request) +.IP "\fBarp_sha\fR" +Copied from the Ethernet source address. +.IP "\fBarp_spa\fR" +Copied from the IP source address. +.IP "\fBarp_tha\fR" +Copied from the Ethernet destination address. +.IP "\fBarp_tpa\fR" +Copied from the IP destination address. +.RE +. .IP \fBdec_ttl\fR .IQ \fBdec_ttl(\fIid1\fR[\fB,\fIid2\fR]...\fB)\fR Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the -- 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev