From patchwork Fri Oct 14 03:57:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zong Kai LI X-Patchwork-Id: 682110 X-Patchwork-Delegate: jpettit@nicira.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (archives.nicira.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 3swDND08kgz9s9N for ; Fri, 14 Oct 2016 14:58:19 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=ySZG9IjB; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id F0E7310685; Thu, 13 Oct 2016 20:57:48 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e4.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id 0800E1067F for ; Thu, 13 Oct 2016 20:57:47 -0700 (PDT) Received: from bar5.cudamail.com (unknown [192.168.21.12]) by mx1e4.cudamail.com (Postfix) with ESMTPS id 95D2E1E04B8 for ; Thu, 13 Oct 2016 21:57:46 -0600 (MDT) X-ASG-Debug-ID: 1476417465-09eadd4cbb10220001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar5.cudamail.com with ESMTP id uXgoU3sPaqDRyqHQ (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 13 Oct 2016 21:57:45 -0600 (MDT) X-Barracuda-Envelope-From: zealokii@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO mail-pf0-f195.google.com) (209.85.192.195) by mx3-pf2.cudamail.com with ESMTPS (AES128-SHA encrypted); 14 Oct 2016 03:57:44 -0000 Received-SPF: pass (mx3-pf2.cudamail.com: SPF record at _netblocks.google.com designates 209.85.192.195 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.85.192.195 X-Barracuda-RBL-IP: 209.85.192.195 Received: by mail-pf0-f195.google.com with SMTP id s8so6304458pfj.2 for ; Thu, 13 Oct 2016 20:57:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=CwkRvqYruIUI7nqMkoON2OXOZKj8GdyZhG2h/rWteXI=; b=ySZG9IjB74W4/9UFU81KSZAiY83bJEPhkxfXuWcSrody0sWJP2NARxaoi1/ACMaWPR zzYUw+cpyKXV7o2WuanxSp4mQ9V/8adZmTG/+owJUcBFcjuUbdDc+GVVfLpeQhdoxENW 3Ky9J2YiwSpz2gsmTPQAfWIopP24b9j5uv2XMZ7wrA0QERT+JO3rS0OqqG+JLAeCOqDs R+8J4SUr9/30rs2YmYi4fvxNIw2zPWLksSCaOmekeZo2c7ks9x2EzJxQlxntP7frOndi PvZ8YaCScoA3pVDQQoVJbMw3YDSILx1mVzvRHPuhMaEsI3i+HxA9hOsKhpVFgaNXwFph rdIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CwkRvqYruIUI7nqMkoON2OXOZKj8GdyZhG2h/rWteXI=; b=GtRv9FuVIWQGJCsEOyNcFrh149Jhrh+O3zmbWFGgS8PDw5aJESPtKLKl0wBMNyguZZ uxdgNd/JfXKrYu7tx00Rj5W+Zt/Yw+gHXz9vepEHqc6i4QzR2JSEjqcjl+r+CbgMHMee 8pwoWbYO6fEgGvhYyXSFze7wHDyn5vUCgSkuptIxNoD0zHGQtf019h8uJnmU96JZlBLM fLvYowznhpFDPsCuxI+6v9IBbA4n0fOzFxZlC+j+1BpCtSbuon+XwavdlYOwHfUJTZWK ZViYFgSVvdx+qbrLeaiau6skpQzGep/BXGk1cvdZCpVL+P/Yzlbt78QLyKLArWwx4Rup 4C3g== X-Gm-Message-State: AA6/9RmIgZlfD1QLlmGHVv3t0Hn1VD2e9++oUNEQC1kSLvMTrpm9i60G3TWkmy9WPJ88dg== X-Received: by 10.98.134.78 with SMTP id x75mr15058782pfd.6.1476417463600; Thu, 13 Oct 2016 20:57:43 -0700 (PDT) Received: from OVN-dev-lzklibj1.ibm.biz ([2401:c900:1201:2a5::2]) by smtp.gmail.com with ESMTPSA id a9sm22897948pfa.38.2016.10.13.20.57.42 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 13 Oct 2016 20:57:43 -0700 (PDT) X-CudaMail-Envelope-Sender: zealokii@gmail.com From: Zongkai LI To: dev@openvswitch.org X-CudaMail-MID: CM-V2-1012066486 X-CudaMail-DTE: 101316 X-CudaMail-Originating-IP: 209.85.192.195 Date: Fri, 14 Oct 2016 03:57:19 +0000 X-ASG-Orig-Subj: [##CM-V2-1012066486##][ovs-dev] [PATCH v3, 5/7] ovn-controller: add 'put_nd_ra' and 'put_nd_opt_*' actions support Message-Id: <1476417441-6239-6-git-send-email-zealokii@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1476417441-6239-1-git-send-email-zealokii@gmail.com> References: <1476417441-6239-1-git-send-email-zealokii@gmail.com> X-GBUdb-Analysis: 0, 209.85.192.195, Ugly c=0.36638 p=-0.2 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1476417465 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using global scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.33701 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Subject: [ovs-dev] [PATCH v3, 5/7] ovn-controller: add 'put_nd_ra' and 'put_nd_opt_*' actions support X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" This patch adds action 'nd_ra' specific inner actions 'put_nd_ra', 'put_nd_opt_prefix', 'put_nd_opt_mtu', 'put_nd_opt_sll'. Action put_nd_ra will set following fields in Router Advertisement (RA) packet: - cur_hop_limit(8-bit unsigned integer), - "Managed address configuration" and "Other configuration" flags(8-bit unsigned integer, with 6-bit 0 in low endian), - router lifetime(16-bit unsigned integer), - reachable time(32-bit unsigned integer), - retrans timer(32-bit unsigned integer). e.g. put_nd_ra(64,0,10800,0,0); Action put_nd_ra_opt_sll will append Source Link-layer Address Option for RA packet with inner ethernet address parameter. e.g. put_nd_ra_opt_sll(12:34:56:78:9a:bc); Action put_nd_ra_opt_mtu will append MTU Option for RA packet with inner integer value(32-bit unsigned integer). e.g. put_nd_ra_opt_mtu(1450); Action put_nd_ra_opt_prefix will append Prefix Information Option with following inner parameters for RA packet: - prefix length(8-bit unsigned integer), - on-link flag and autonomous address-configuration flag (8-bit unsigned integer, with 6-bit 0 in low endian), - valid lifetime(32-bit unsigned integer), - preferred lifetime(32-bit unsigned integer), - prefix(128-bit IPv6 address prefix). e.g. put_nd_ra_opt_prefix(64,192,10800,10800,fdad:a0f9:a012::); Signed-off-by: Zongkai LI --- include/ovn/actions.h | 79 +++++++++++++- ovn/controller/pinctrl.c | 186 ++++++++++++++++++++++++++++++-- ovn/lib/actions.c | 264 +++++++++++++++++++++++++++++++++++++++++++++- ovn/ovn-sb.xml | 39 +++++++ ovn/utilities/ovn-trace.c | 4 + tests/ovn.at | 16 ++- 6 files changed, 575 insertions(+), 13 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index f5f1e62..91d968e 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -69,7 +69,11 @@ struct simap; OVNACT(PUT_ND, ovnact_put_mac_bind) \ OVNACT(PUT_DHCPV4_OPTS, ovnact_put_dhcp_opts) \ OVNACT(PUT_DHCPV6_OPTS, ovnact_put_dhcp_opts) \ - OVNACT(SET_QUEUE, ovnact_set_queue) + OVNACT(SET_QUEUE, ovnact_set_queue) \ + OVNACT(PUT_ND_RA, ovnact_put_nd_ra) \ + OVNACT(PUT_ND_OPT_SLL, ovnact_put_nd_opt_sll) \ + OVNACT(PUT_ND_OPT_MTU, ovnact_put_nd_opt_mtu) \ + OVNACT(PUT_ND_OPT_PREFIX, ovnact_put_nd_opt_prefix) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -235,6 +239,38 @@ struct ovnact_set_queue { uint16_t queue_id; }; +/* OVNACT_PUT_ND_RA. */ +struct ovnact_put_nd_ra { + struct ovnact ovnact; + uint8_t cur_hop_limit; + uint8_t mo_flags; + uint16_t router_lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; +}; + +/* OVNACT_PUT_ND_OPT_SLL. */ +struct ovnact_put_nd_opt_sll { + struct ovnact ovnact; + struct eth_addr mac; +}; + +/* OVNACT_PUT_ND_OPT_MTU. */ +struct ovnact_put_nd_opt_mtu { + struct ovnact ovnact; + uint32_t mtu; +}; + +/* OVNACT_PUT_ND_OPT_PREFIX. */ +struct ovnact_put_nd_opt_prefix { + struct ovnact ovnact; + uint8_t prefix_len; + uint8_t la_flags; + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + struct in6_addr prefix; +}; + /* Internal use by the helpers below. */ void ovnact_init(struct ovnact *, enum ovnact_type, size_t len); void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len); @@ -368,6 +404,44 @@ enum action_opcode { * The actions, in OpenFlow 1.3 format, follow the action_header. */ ACTION_OPCODE_ND_RA, + + /* "put_nd_ra(c, mo, router_lt, reach_t, retrans_t)". + * + * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format: + * - c: 8-bit unsigned integer for current hop limit. + * - mo: 8-bit unsigned integer for Managed address configuration flag + * and Other configuration flag. (including 6-bit reserved 0) + * - router_lt: 16-bit unsigned integer for router lifetime. + * - reach_t: 32-bit unisgned integer for reachable time. + * - retrans_t: 32-bit unsigned integer for retrans timer. + */ + ACTION_OPCODE_PUT_ND_RA, + + /* "put_nd_opt_sll(mac)". + * + * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format: + * - mac: 48-bit ethernet address. + */ + ACTION_OPCODE_PUT_ND_OPT_SLL, + + /* "put_nd_opt_mtu(mtu)". + * + * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format: + * - mtu: 32-bit unsigned integer MTU. + */ + ACTION_OPCODE_PUT_ND_OPT_MTU, + + /* "put_nd_opt_prefix(plen, la, valid_lt, preferred_lt, prefix)". + * + * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format: + * - plen: 8-bit unsigned integer for prefix length. + * - la: 8-bit unsigned integer for on-link flag and autonomous + * address-configuration flag. (including 6-bit reserved 0) + * - valid_lt: 32-bit unsigned integer for valid_lifetime. + * - preferred_lt: 32-bit unsigned integer for preferred_lifetime. + * - prefix: 128-bit IPv6 address. + */ + ACTION_OPCODE_PUT_ND_OPT_PREFIX, }; /* Header. */ @@ -453,6 +527,9 @@ struct ovnact_encode_params { resubmit. */ }; +void ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts); + void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, const struct ovnact_encode_params *, struct ofpbuf *ofpacts); diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index cfeb401..fe71c48 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -1357,6 +1357,114 @@ reload_metadata(struct ofpbuf *ofpacts, const struct match *md) } } +static void try_parse_nd_ra_args(struct ofpbuf *userdata, uint8_t *chl, + uint8_t *mo, ovs_be16 *rlt, + ovs_be32 *rcht, ovs_be32 *rtrt, + uint16_t *args_left_size) +{ + int put_nd_ra_args_len = + sizeof(uint8_t) * 2 + sizeof(uint16_t) + sizeof(uint32_t) * 2; + if (*args_left_size < put_nd_ra_args_len) { + return; + } + uint8_t *cur_hop_limit = ofpbuf_try_pull(userdata, sizeof(uint8_t)); + if (!cur_hop_limit) { + return; + } + uint8_t *mo_flags = ofpbuf_try_pull(userdata, sizeof(uint8_t)); + if (!mo_flags) { + return; + } + ovs_be16 *router_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be16)); + if (!router_lifetime) { + return; + } + ovs_be32 *reachable_time = ofpbuf_try_pull(userdata, sizeof(ovs_be32)); + if (!reachable_time) { + return; + } + ovs_be32 *retrans_timer = ofpbuf_try_pull(userdata, sizeof(ovs_be32)); + if (!retrans_timer) { + return; + } + *chl = *cur_hop_limit; + *mo = *mo_flags; + *rlt = *router_lifetime; + *rcht = *reachable_time; + *rtrt = *retrans_timer; + *args_left_size -= put_nd_ra_args_len; +} + +static void try_parse_nd_prefix_opt_arg(struct ofpbuf *userdata, int *opt_index, + uint8_t *plen, uint8_t *la, ovs_be32 *vl, + ovs_be32 *pl, ovs_be128 *p, + uint16_t *args_left_size) +{ + int put_nd_prefix_opt_args_len = + sizeof(uint8_t) * 2 + sizeof(uint32_t) *2 + sizeof(struct in6_addr); + if (*args_left_size < put_nd_prefix_opt_args_len) { + return; + } + uint8_t *prefix_len = ofpbuf_try_pull(userdata, sizeof(uint8_t)); + if (!prefix_len || *prefix_len == 0 || *prefix_len == 128) { + return; + } + uint8_t *la_flags = ofpbuf_try_pull(userdata, sizeof(uint8_t)); + if (!la_flags) { + return; + } + ovs_be32 *valid_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be32)); + if (!valid_lifetime || *valid_lifetime == 0) { + return; + } + ovs_be32 *preferred_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be32)); + if (!preferred_lifetime || *preferred_lifetime == 0) { + return; + } + ovs_be128 *prefix = ofpbuf_try_pull(userdata, sizeof(ovs_be128)); + if (!prefix) { + return; + } + plen[*opt_index] = *prefix_len; + la[*opt_index] = *la_flags; + vl[*opt_index] = *valid_lifetime; + pl[*opt_index] = *preferred_lifetime; + p[*opt_index] = *prefix; + (*opt_index)++; + *args_left_size -= put_nd_prefix_opt_args_len; +} + +static void try_parse_nd_sll_opt_arg(struct ofpbuf *userdata, + struct eth_addr *e, + uint16_t *args_left_size) +{ + if (*args_left_size < sizeof(struct eth_addr)) { + return; + } + struct eth_addr *sll = ofpbuf_try_pull(userdata, sizeof(struct eth_addr)); + if (!sll) { + return; + } + memcpy(e, sll, sizeof(struct eth_addr)); + *args_left_size -= sizeof(struct eth_addr); +} + +static void +try_parse_nd_mtu_opt_arg(struct ofpbuf *userdata, + ovs_be32 *m, uint16_t *args_left_size) +{ + if (*args_left_size < sizeof(ovs_be32)) { + return; + } + ovs_be32 *mtu = ofpbuf_try_pull(userdata, sizeof(ovs_be32)); + /* Per RFC 2460, consider 1280 as minimum IPv6 MTU. */ + if (!mtu || *mtu < 1280) { + return; + } + *m = *mtu; + *args_left_size -= sizeof(ovs_be32); +} + static void pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md, struct ofpbuf *userdata) @@ -1392,20 +1500,82 @@ pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md, &ip_flow->nd_target, &ip_flow->ipv6_src, htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE)); } else { - /* xxx Using hardcode data to compose RA packet. - * xxx The following patch should fix this. */ uint8_t cur_hop_limit = 64; uint8_t mo_flags = 0; ovs_be16 router_lifetime = htons(0x2a30); /* 3 hours. */ ovs_be32 reachable_time = 0; ovs_be32 retrans_timer = 0; - ovs_be32 mtu = 0; - compose_nd_ra_with_sll_mtu_opts( - &packet, ip_flow->dl_dst, ip_flow->dl_src, - &ip_flow->ipv6_dst, &ip_flow->ipv6_src, - cur_hop_limit, mo_flags, router_lifetime, reachable_time, - retrans_timer, mtu); + uint16_t *args_len_p = ofpbuf_try_pull(userdata, sizeof(uint16_t)); + uint16_t args_len = args_len_p ? ntohs(*args_len_p) : 0; + uint8_t *arg = NULL; + if (args_len > 0) { + struct eth_addr sll = eth_addr_zero; + ovs_be32 mtu = 0; + + int prefix_opt_size = sizeof(uint8_t) * 3 + sizeof(ovs_be32) * 3; + int n_prefix_opts = args_len / prefix_opt_size; + int cur_prefix_opt = 0; + uint8_t plen[n_prefix_opts]; + uint8_t la_flags[n_prefix_opts]; + ovs_be32 valid_lt[n_prefix_opts]; + ovs_be32 preferred_lt[n_prefix_opts]; + ovs_be128 prefix[n_prefix_opts]; + + while (args_len > 0) { + arg = ofpbuf_try_pull(userdata, sizeof(uint8_t)); + args_len -= sizeof(uint8_t); + if (arg) { + switch(*arg) { + case ACTION_OPCODE_PUT_ND_RA: + try_parse_nd_ra_args(userdata, &cur_hop_limit, + &mo_flags, &router_lifetime, + &reachable_time, &retrans_timer, + &args_len); + break; + + case ACTION_OPCODE_PUT_ND_OPT_PREFIX: + try_parse_nd_prefix_opt_arg(userdata, &cur_prefix_opt, + plen, la_flags, valid_lt, + preferred_lt, prefix, + &args_len); + break; + + case ACTION_OPCODE_PUT_ND_OPT_SLL: + try_parse_nd_sll_opt_arg(userdata, &sll, &args_len); + break; + + case ACTION_OPCODE_PUT_ND_OPT_MTU: + try_parse_nd_mtu_opt_arg(userdata, &mtu, &args_len); + break; + + default: + VLOG_WARN_RL(&rl, "Unknown to parse inner arguments" + " for 'nd_ra'"); + break; + } + } else { + VLOG_WARN_RL(&rl, "Fail to parse inner arguments for" + " 'nd_ra'"); + break; + } + } + + if (eth_addr_is_zero(sll)) { + goto exit; + } + + compose_nd_ra_with_sll_mtu_opts( + &packet, sll, ip_flow->dl_src, + &ip_flow->ipv6_dst, &ip_flow->ipv6_src, + cur_hop_limit, mo_flags, router_lifetime, reachable_time, + retrans_timer, mtu); + for (int i = 0; i != cur_prefix_opt; i++) { + packet_put_ra_prefix_opt(&packet, plen[i], la_flags[i], + valid_lt[i], preferred_lt[i], + prefix[i]); + } + } } /* Reload previous packet metadata. */ diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index e6d6e51..60fa4b0 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1151,7 +1151,45 @@ encode_ND_RA(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ND_RA, ofpacts); + uint64_t inner_ofpacts_stub[1024 / 8]; + uint64_t inner_args_stub[1024 / 8]; + struct ofpbuf inner_args = OFPBUF_STUB_INITIALIZER(inner_args_stub); + struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub); + const struct ovnact *a; + OVNACT_FOR_EACH (a, on->nested, on->nested_len) { + if (a->type == OVNACT_PUT_ND_RA) { + encode_PUT_ND_RA( + (const struct ovnact_put_nd_ra *)a, ep, &inner_args); + } else if (a->type == OVNACT_PUT_ND_OPT_SLL) { + encode_PUT_ND_OPT_SLL( + (const struct ovnact_put_nd_opt_sll *)a, ep, &inner_args); + } else if (a->type == OVNACT_PUT_ND_OPT_MTU) { + encode_PUT_ND_OPT_MTU( + (const struct ovnact_put_nd_opt_mtu *)a, ep, &inner_args); + } else if (a->type == OVNACT_PUT_ND_OPT_PREFIX) { + encode_PUT_ND_OPT_PREFIX( + (const struct ovnact_put_nd_opt_prefix *)a, ep, &inner_args); + } else { + ovnact_encode(a, ep, &inner_ofpacts); + } + } + uint16_t inner_args_size = inner_args.size ? htons(inner_args.size) : 0; + + /* Add a "controller" action with the data and actions parsed from nested + * actions inside "{...}", converted to OpenFlow, as its userdata. + * ovn-controller will convert the packet to RA and then send the packet + * and actions back to the switch inside an OFPT_PACKET_OUT message. */ + size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_ND_RA, + false, ofpacts); + ofpbuf_put(ofpacts, &inner_args_size, sizeof(uint16_t)); + ofpbuf_put(ofpacts, inner_args.data, inner_args.size); + ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, + ofpacts, OFP13_VERSION); + encode_finish_controller_op(oc_offset, ofpacts); + + /* Free memory. */ + ofpbuf_uninit(&inner_args); + ofpbuf_uninit(&inner_ofpacts); } static void @@ -1710,7 +1748,220 @@ parse_set_action(struct action_context *ctx) lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'"); } } + +/* Parse put_nd_ra, put_nd_opt_sll, put_nd_prefix, put_nd_mtu action. */ +static void +parse_put_nd_ra(struct action_context *ctx, + struct ovnact_put_nd_ra *put_ra) +{ + lexer_force_match(ctx->lexer, LEX_T_LPAREN); + int field = 0; + int n_fields = 5; + while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { + if (!lexer_match(ctx->lexer, LEX_T_INTEGER) + || ctx->lexer->token.format != LEX_F_DECIMAL) { + lexer_syntax_error(ctx->lexer, "expecting demical integer"); + } + switch (field) { + case 0: + put_ra->cur_hop_limit = ctx->lexer->token.value.u8_val; + break; + case 1: + put_ra->mo_flags = ctx->lexer->token.value.u8_val; + break; + case 2: + put_ra->router_lifetime = ntohs( + ctx->lexer->token.value.be16_int); + break; + case 3: + put_ra->reachable_time = ntohl( + ctx->lexer->token.value.be32_int); + break; + case 4: + put_ra->retrans_timer = ntohl( + ctx->lexer->token.value.be32_int); + break; + default: + lexer_syntax_error(ctx->lexer, "not many fields parsed"); + } + lexer_match(ctx->lexer, LEX_T_COMMA); + field++; + } + if (field != n_fields) { + lexer_syntax_error(ctx->lexer, "not enough fields parsed"); + } +} +static void +parse_put_nd_opt_prefix(struct action_context *ctx, + struct ovnact_put_nd_opt_prefix *prefix) +{ + lexer_force_match(ctx->lexer, LEX_T_LPAREN); + + int field = 0; + int n_fields = 5; + while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { + if (!lexer_match(ctx->lexer, LEX_T_INTEGER)) { + lexer_syntax_error(ctx->lexer, "expecting integer"); + } else { + if (field < 4 && ctx->lexer->token.format != LEX_F_DECIMAL) { + lexer_syntax_error(ctx->lexer, "expecting demical integer"); + } else if (field == 4 && ctx->lexer->token.format != LEX_F_IPV6) { + lexer_syntax_error(ctx->lexer, "expecting IPv6 address"); + } + } + + switch (field) { + case 0: + prefix->prefix_len = ctx->lexer->token.value.u8_val; + break; + case 1: + prefix->la_flags = ctx->lexer->token.value.u8_val; + break; + case 2: + prefix->valid_lifetime = + ntohl(ctx->lexer->token.value.be32_int); + break; + case 3: + prefix->preferred_lifetime = ntohl( + ctx->lexer->token.value.be32_int); + break; + case 4: + memcpy(&prefix->prefix, &ctx->lexer->token.value.ipv6, + sizeof(struct in6_addr)); + break; + default: + lexer_syntax_error(ctx->lexer, "not many fields parsed"); + } + lexer_match(ctx->lexer, LEX_T_COMMA); + field++; + } + if (field != n_fields) { + lexer_syntax_error(ctx->lexer, "not enough fields parsed"); + } +} +static void +parse_put_nd_opt_sll(struct action_context *ctx, + struct ovnact_put_nd_opt_sll *sll) +{ + lexer_force_match(ctx->lexer, LEX_T_LPAREN); + if (!lexer_match(ctx->lexer, LEX_T_INTEGER) + || ctx->lexer->token.format != LEX_F_ETHERNET) { + lexer_syntax_error(ctx->lexer, "expecting ether address"); + } + sll->mac = ctx->lexer->token.value.mac; + lexer_force_match(ctx->lexer, LEX_T_RPAREN); +} +static void +parse_put_nd_opt_mtu(struct action_context *ctx, + struct ovnact_put_nd_opt_mtu *mtu) +{ + lexer_force_match(ctx->lexer, LEX_T_LPAREN); + if (!lexer_match(ctx->lexer, LEX_T_INTEGER) + || ctx->lexer->token.format != LEX_F_DECIMAL) { + lexer_syntax_error(ctx->lexer, "expecting decimal integer"); + } + mtu->mtu = ntohl(ctx->lexer->token.value.be32_int); + lexer_force_match(ctx->lexer, LEX_T_RPAREN); +} + +static void +format_PUT_ND_RA(const struct ovnact_put_nd_ra *ra, struct ds *s) +{ + ds_put_format(s, "put_nd_ra(%u, %u, %u, %u, %u);", + ra->cur_hop_limit, ra->mo_flags, ra->router_lifetime, + ra->reachable_time, ra->retrans_timer); +} +static void +format_PUT_ND_OPT_PREFIX(const struct ovnact_put_nd_opt_prefix *p, + struct ds *s) +{ + ds_put_format(s, "put_nd_opt_prefix(%u, %u, %u, %u, ", + p->prefix_len, p->la_flags, p->valid_lifetime, + p->preferred_lifetime); + ipv6_format_addr(&p->prefix, s); + ds_put_cstr(s, ");"); +} +static void +format_PUT_ND_OPT_SLL(const struct ovnact_put_nd_opt_sll *sll, struct ds *s) +{ + ds_put_cstr(s, "put_nd_opt_sll("); + ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(sll->mac)); + ds_put_cstr(s, ");"); +} +static void +format_PUT_ND_OPT_MTU(const struct ovnact_put_nd_opt_mtu *mtu, struct ds *s) +{ + ds_put_format(s, "put_nd_opt_mtu(%u);", mtu->mtu); +} + +static void +encode_PUT_ND_RA(const struct ovnact_put_nd_ra *ra, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + uint8_t inner_code = ACTION_OPCODE_PUT_ND_RA; + ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t)); + ofpbuf_put(ofpacts, &ra->cur_hop_limit, sizeof(uint8_t)); + ofpbuf_put(ofpacts, &ra->mo_flags, sizeof(uint8_t)); + uint16_t rlt = htons(ra->router_lifetime); + uint32_t rt = htonl(ra->reachable_time); + uint32_t rtt = htonl(ra->retrans_timer); + ofpbuf_put(ofpacts, &rlt, sizeof(uint16_t)); + ofpbuf_put(ofpacts, &rt, sizeof(uint32_t)); + ofpbuf_put(ofpacts, &rtt, sizeof(uint32_t)); +} +static void +encode_PUT_ND_OPT_PREFIX(const struct ovnact_put_nd_opt_prefix *prefix, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_PREFIX; + ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t)); + ofpbuf_put(ofpacts, &prefix->prefix_len, sizeof(uint8_t)); + ofpbuf_put(ofpacts, &prefix->la_flags, sizeof(uint8_t)); + uint32_t vlt = htonl(prefix->valid_lifetime); + uint32_t plt = htonl(prefix->preferred_lifetime); + ofpbuf_put(ofpacts, &vlt, sizeof(uint32_t)); + ofpbuf_put(ofpacts, &plt, sizeof(uint32_t)); + ofpbuf_put(ofpacts, &prefix->prefix, sizeof(struct in6_addr)); +} +static void +encode_PUT_ND_OPT_SLL(const struct ovnact_put_nd_opt_sll *sll, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_SLL; + ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t)); + ofpbuf_put(ofpacts, &sll->mac, sizeof(struct eth_addr)); +} +static void +encode_PUT_ND_OPT_MTU(const struct ovnact_put_nd_opt_mtu *mtu, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_MTU; + ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t)); + uint32_t m = htonl(mtu->mtu); + ofpbuf_put(ofpacts, &m, sizeof(uint32_t)); +} +static void +free_PUT_ND_RA(struct ovnact_put_nd_ra *ra OVS_UNUSED) +{ +} +static void +free_PUT_ND_OPT_PREFIX(struct ovnact_put_nd_opt_prefix *prefix OVS_UNUSED) +{ +} +static void +free_PUT_ND_OPT_SLL(struct ovnact_put_nd_opt_sll *sll OVS_UNUSED) +{ +} +static void +free_PUT_ND_OPT_MTU(struct ovnact_put_nd_opt_mtu *mtu OVS_UNUSED) +{ +} + static bool parse_action(struct action_context *ctx) { @@ -1745,6 +1996,15 @@ parse_action(struct action_context *ctx) parse_ND_NA(ctx); } else if (lexer_match_id(ctx->lexer, "nd_ra")) { parse_ND_RA(ctx); + } else if (lexer_match_id(ctx->lexer, "put_nd_ra")) { + parse_put_nd_ra(ctx, ovnact_put_PUT_ND_RA(ctx->ovnacts)); + } else if (lexer_match_id(ctx->lexer, "put_nd_opt_prefix")) { + parse_put_nd_opt_prefix(ctx, + ovnact_put_PUT_ND_OPT_PREFIX(ctx->ovnacts)); + } else if (lexer_match_id(ctx->lexer, "put_nd_opt_sll")) { + parse_put_nd_opt_sll(ctx, ovnact_put_PUT_ND_OPT_SLL(ctx->ovnacts)); + } else if (lexer_match_id(ctx->lexer, "put_nd_opt_mtu")) { + parse_put_nd_opt_mtu(ctx, ovnact_put_PUT_ND_OPT_MTU(ctx->ovnacts)); } else if (lexer_match_id(ctx->lexer, "get_arp")) { parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts)); } else if (lexer_match_id(ctx->lexer, "put_arp")) { @@ -1884,7 +2144,7 @@ ovnacts_format(const struct ovnact *ovnacts, size_t ovnacts_len, /* Encoding ovnacts to OpenFlow. */ -static void +void ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index e945343..1010722 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1241,6 +1241,45 @@

+ The 'nd_ra' action has specific inner action + put_nd_ra(h,mo,lt,ct,tr) to hold values for fields + and flags in RA message. + Parameters: 8-bit unsigned integer cur_hop_limit field + h, 8-bit unsigned integer for "Managed address + configuration" and "Other configuration" flags with 6-bit reserved + 0 in field mo, 16-bit unsigned integer router_lifetime + field lt, 32-bit unsigned integer reachable_time + field ct, 32-bit unsigned integer retrans_timer + field tr. +

+ +

+ The 'nd_ra' action has specific inner action + put_nd_opt_prefix(pl,la,vt,pt,p) to hold values for + fields and flags in Prefix Information Option for RA message. + Parameters: 8-bit unsigned integer prefix lenght field + pl, 8-bit unsigned integer for on-link flag and + autonomous address-configuration flag with 6-bit reserved 0 in + field mo, 32-bit unsigned integer valid_lifetime field + vt, 32-bit unsigned integer preferred_lifetime field + pt, 128-bit IPv6 prefix field p. +

+ +

+ The 'nd_ra' action has specific inner action + put_nd_opt_mtu(m) to hold value for mtu field in MTU + Option for RA message. + Parameters: 32-bit unsigned integer mtu field m. +

+ +

+ The 'nd_ra' action has specific inner action + put_nd_opt_sll(s) to hold value for link-layer address + field in Source Link-layer Address Option for RA message. + Parameters: 48-bit Ethernet address field s. +

+ +

The ND packet has the same VLAN header, if any, as the IPv6 packet it replaces.

diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 80b1af0..8749820 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -1285,6 +1285,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_PUT_ARP: case OVNACT_PUT_ND: + case OVNACT_PUT_ND_RA: + case OVNACT_PUT_ND_OPT_PREFIX: + case OVNACT_PUT_ND_OPT_SLL: + case OVNACT_PUT_ND_OPT_MTU: /* Nothing to do for tracing. */ break; diff --git a/tests/ovn.at b/tests/ovn.at index 5d599c8..85f75d0 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -915,9 +915,21 @@ nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inpor has prereqs nd_ns # nd_ra -nd_ra{eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output;}; +nd_ra { eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output;}; formats as nd_ra { eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output; }; - encodes as controller(userdata=00.00.00.06.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.18.80.00.34.10.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.01.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) + encodes as controller(userdata=00.00.00.06.00.00.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.18.80.00.34.10.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.01.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) + has prereqs nd_rs +nd_ra { put_nd_ra(64,0,10800,0,0);}; + formats as nd_ra { put_nd_ra(64, 0, 10800, 0, 0); }; + encodes as controller(userdata=00.00.00.06.00.00.00.00.00.0d.07.40.00.2a.30.00.00.00.00.00.00.00.00) + has prereqs nd_rs +nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64,0,10800,10800,fdad:1234:5678::);}; + formats as nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64, 0, 10800, 10800, fdad:1234:5678::); }; + encodes as controller(userdata=00.00.00.06.00.00.00.00.00.27.08.12.34.56.78.9a.bc.09.00.00.05.00.0a.40.00.00.00.2a.30.00.00.2a.30.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00) + has prereqs nd_rs +nd_ra { put_nd_ra(64,0,10800,0,0); put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64,0,10800,10800,fdad:1234:5678::); eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; }; + formats as nd_ra { put_nd_ra(64, 0, 10800, 0, 0); put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64, 0, 10800, 10800, fdad:1234:5678::); eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; }; + encodes as controller(userdata=00.00.00.06.00.00.00.00.00.34.07.40.00.2a.30.00.00.00.00.00.00.00.00.08.12.34.56.78.9a.bc.09.00.00.05.00.0a.40.00.00.00.2a.30.00.00.2a.30.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) has prereqs nd_rs # get_nd