From patchwork Tue Nov 24 11:19:02 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Babu Shanmugam X-Patchwork-Id: 548010 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (unknown [IPv6:2600:3c00::f03c:91ff:fe6e:bdf7]) by ozlabs.org (Postfix) with ESMTP id EE0BD1402D7 for ; Tue, 24 Nov 2015 22:19:43 +1100 (AEDT) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 3F1B4108C3; Tue, 24 Nov 2015 03:19:37 -0800 (PST) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id BACE9108C1 for ; Tue, 24 Nov 2015 03:19:35 -0800 (PST) Received: from bar3.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 44FEB161A60 for ; Tue, 24 Nov 2015 04:19:35 -0700 (MST) X-ASG-Debug-ID: 1448363973-03dd7b46442c6a70001-byXFYA Received: from mx3-pf3.cudamail.com ([192.168.14.3]) by bar3.cudamail.com with ESMTP id fFiSGBFPZ84S2Jfz (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 24 Nov 2015 04:19:33 -0700 (MST) X-Barracuda-Envelope-From: bschanmu@redhat.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.3 Received: from unknown (HELO mx1.redhat.com) (209.132.183.28) by mx3-pf3.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted); 24 Nov 2015 11:22:01 -0000 Received-SPF: pass (mx3-pf3.cudamail.com: SPF record at _spf1.redhat.com designates 209.132.183.28 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.132.183.28 X-Barracuda-RBL-IP: 209.132.183.28 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id 0F941C0F1D1B for ; Tue, 24 Nov 2015 11:19:23 +0000 (UTC) Received: from anbu-ltp.redhat.com (ovpn-113-32.phx2.redhat.com [10.3.113.32]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tAOBJ3Ap019633; Tue, 24 Nov 2015 06:19:16 -0500 X-CudaMail-Envelope-Sender: bschanmu@redhat.com From: bschanmu@redhat.com To: dev@openvswitch.org X-CudaMail-MID: CM-V3-1123005580 X-CudaMail-DTE: 112415 X-CudaMail-Originating-IP: 209.132.183.28 Date: Tue, 24 Nov 2015 16:49:02 +0530 X-ASG-Orig-Subj: [##CM-V3-1123005580##][PATCH v4 3/4] ovn: Process dhcp packet-ins and respond through packet-outs Message-Id: <1448363942-9857-4-git-send-email-bschanmu@redhat.com> In-Reply-To: <1448363942-9857-1-git-send-email-bschanmu@redhat.com> References: <1448363942-9857-1-git-send-email-bschanmu@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-GBUdb-Analysis: 0, 209.132.183.28, Ugly c=0 p=0 Source New X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.3] X-Barracuda-Start-Time: 1448363973 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 per-user scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, NO_REAL_NAME, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.24677 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 NO_REAL_NAME From: does not include a real name 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 v4 3/4] ovn: Process dhcp packet-ins and respond through packet-outs 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" The DHCP packets can be of two types (1) DHCP Discover (2) DHCP Request For (1), the controller should respond with DHCP offer and for (2), either DHCP Ack or DHCP Nack should be sent. In this patch, DHCP Nack is never sent. In case of failures in validating the packet, the controller does not respond at all. For a DHCP packet, the IP address is read from the port binding table with the source MAC as the search reference. The DHCP options for netmask and router are expected with key values dhcp-opt-1 and dhcp-opt-3 respectively in port binding table - options column. In case of the absence of these options from logical port entry, dhcp packet is flooded. Signed-off-by: Babu Shanmugam Signed-off-by: Numan Siddique Co-Authored-by: Numan Siddique --- ovn/controller/automake.mk | 2 + ovn/controller/ovn-dhcp.c | 493 +++++++++++++++++++++++++++++++++++++++++++++ ovn/controller/ovn-dhcp.h | 34 ++++ ovn/controller/pinctrl.c | 43 +++- ovn/ovn-nb.xml | 29 +++ ovn/ovn-sb.xml | 29 +++ 6 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 ovn/controller/ovn-dhcp.c create mode 100644 ovn/controller/ovn-dhcp.h diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk index cadfa9c..fdd6788 100644 --- a/ovn/controller/automake.mk +++ b/ovn/controller/automake.mk @@ -16,6 +16,8 @@ ovn_controller_ovn_controller_SOURCES = \ ovn/controller/patch.h \ ovn/controller/ovn-controller.c \ ovn/controller/ovn-controller.h \ + ovn/controller/ovn-dhcp.c \ + ovn/controller/ovn-dhcp.h \ ovn/controller/physical.c \ ovn/controller/physical.h ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la diff --git a/ovn/controller/ovn-dhcp.c b/ovn/controller/ovn-dhcp.c new file mode 100644 index 0000000..db4442b --- /dev/null +++ b/ovn/controller/ovn-dhcp.c @@ -0,0 +1,493 @@ + +/* Copyright (c) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "csum.h" +#include "dp-packet.h" +#include "dhcp.h" +#include "lib/dhcp.h" +#include "ofpbuf.h" +#include "ofp-actions.h" +#include "ofp-util.h" +#include "ovn-controller.h" +#include "ovn-dhcp.h" + +#define DHCP_SERVER_ID ((uint32_t)0x01010101) +#define DHCP_LEASE_PERIOD ((uint32_t)60*60*24) /* 1 day */ + +#define DHCP_MAGIC_COOKIE (uint32_t)0x63825363 + +#define DHCP_DEFAULT_NETMASK (uint32_t)0xFFFFFF00 + +#define DHCP_DEFAULT_IFACE_MTU (uint16_t)1400 + +#define DHCP_OP_REQUEST ((uint8_t)1) +#define DHCP_OP_REPLY ((uint8_t)2) + +#define DHCP_MSG_DISCOVER ((uint8_t)1) +#define DHCP_MSG_OFFER ((uint8_t)2) +#define DHCP_MSG_REQUEST ((uint8_t)3) +#define DHCP_MSG_ACK ((uint8_t)5) +#define DHCP_MSG_NACK ((uint8_t)6) + +/* DHCP options */ +#define DHCP_OPT_NETMASK ((uint8_t)1) +#define DHCP_OPT_ROUTER ((uint8_t)3) +#define DHCP_OPT_DNS_SERVER ((uint8_t)6) +#define DHCP_OPT_LOG_SERVER ((uint8_t)7) +#define DHCP_OPT_LPR_SERVER ((uint8_t)9) +#define DHCP_OPT_BOOT_FILE_SIZE ((uint8_t)13) +#define DHCP_OPT_DOMAIN_NAME ((uint8_t)15) +#define DHCP_OPT_SWAP_SERVER ((uint8_t)16) +#define DHCP_OPT_IP_FORWARD_ENABLE ((uint8_t)19) +#define DHCP_OPT_POLICY_FILTER ((uint8_t)21) +#define DHCP_OPT_DEFAULT_TTL ((uint8_t)23) +#define DHCP_OPT_IFACE_MTU ((uint8_t)26) +#define DHCP_OPT_RD ((uint8_t)31) +#define DHCP_OPT_ROUTER_SOLICITATION ((uint8_t)32) +#define DHCP_OPT_ARP_TIMEOUT ((uint8_t)35) +#define DHCP_OPT_ETH_ENCAP ((uint8_t)36) +#define DHCP_OPT_TCP_TTL ((uint8_t)37) +#define DHCP_OPT_TCP_KEEPALIVE ((uint8_t)38) +#define DHCP_OPT_NIS_DOMAIN ((uint8_t)40) +#define DHCP_OPT_NIS_SERVER ((uint8_t)41) +#define DHCP_OPT_NTP_SERVER ((uint8_t)42) +#define DHCP_OPT_ADDR_REQ ((uint8_t)50) +#define DHCP_OPT_LEASE_TIME ((uint8_t)51) +#define DHCP_OPT_MSG_TYPE ((uint8_t)53) +#define DHCP_OPT_SERVER_ID ((uint8_t)54) +#define DHCP_OPT_PARAMS ((uint8_t)55) +#define DHCP_OPT_TFTP_SERVER ((uint8_t)66) +#define DHCP_OPT_END ((uint8_t)255) + +#define DHCP_OPT_NETMASK_KEY "dhcp-opt-1" +#define DHCP_OPT_ROUTER_KEY "dhcp-opt-3" + +struct dhcp_packet_ctx { + struct controller_ctx *ctrl_ctx; + struct ofputil_packet_in *pin; + struct flow *flow; + struct dp_packet *packet; + const struct sbrec_port_binding *binding; + uint8_t message_type; + ovs_be32 requested_ipv4; + ovs_be32 offered_ipv4; +}; + +struct dhcp_option_header { + uint8_t option; + uint8_t len; +}; + +#define OPTION_PAYLOAD(opt) ((char *)opt + sizeof(struct dhcp_option_header)) +#define OPTION_PAYLOAD_END(opt) ((char *)opt + \ + sizeof(struct dhcp_option_header) + opt->len) + +static int +add_option_to_dhcp_resp_buf(char *buf, uint8_t dhcp_option, char *option_data) +{ + struct dhcp_option_header *opt; + int ret_len = 0; + switch(dhcp_option) { + case DHCP_OPT_NETMASK: + case DHCP_OPT_ROUTER: + case DHCP_OPT_DNS_SERVER: + case DHCP_OPT_LOG_SERVER: + case DHCP_OPT_LPR_SERVER: + case DHCP_OPT_SWAP_SERVER: + case DHCP_OPT_POLICY_FILTER: + case DHCP_OPT_ROUTER_SOLICITATION: + case DHCP_OPT_NIS_SERVER: + case DHCP_OPT_NTP_SERVER: + case DHCP_OPT_TFTP_SERVER: + { + /* option value type - ip address(es) */ + ovs_be32 ip_addr; + opt = (struct dhcp_option_header *) buf; + opt->option = dhcp_option; + opt->len = 0; + int scan_len = 0; + ret_len = 2; + /* option value can have multiple ip addresses. + * Eg. dhcp-opt-6=8.8.8.8 10.10.10.10" for DNS option + */ + while(ovs_scan_len(option_data, &scan_len, IP_SCAN_FMT, + IP_SCAN_ARGS(&ip_addr))) { + *((uint32_t *) OPTION_PAYLOAD_END(opt)) = ip_addr; + opt->len += sizeof(ovs_be32); + option_data += (scan_len + 1); + ret_len += 4; + scan_len = 0; + } + break; + } + /* option value type - bool */ + case DHCP_OPT_IP_FORWARD_ENABLE: + case DHCP_OPT_ETH_ENCAP: + case DHCP_OPT_RD: + /* option value type - uint8_t */ + case DHCP_OPT_DEFAULT_TTL: + case DHCP_OPT_TCP_TTL: + { + buf[0] = dhcp_option; + buf[1] = 1; + uint8_t value; + if (ovs_scan(option_data, "%hhu", &value)) { + ret_len = 3; + buf[2] = (uint8_t) value; + } + break; + } + /* option value type - uint16_t */ + case DHCP_OPT_BOOT_FILE_SIZE: + case DHCP_OPT_IFACE_MTU: + { + buf[0] = dhcp_option; + buf[1] = 2; + uint16_t value; + if (ovs_scan(option_data, "%h"PRIu16, &value)) { + ret_len = 4; + (*(uint16_t *)&buf[2]) = htons(value); + } + break; + } + /* option value type - uint32_t */ + case DHCP_OPT_ARP_TIMEOUT: + case DHCP_OPT_TCP_KEEPALIVE: + buf[0] = dhcp_option; + buf[1] = 4; + uint32_t value; + if (ovs_scan(option_data, "%"PRIu32, &value)) { + ret_len = 6; + (*(uint32_t *)&buf[2]) = htonl(value); + } + break; + + /* option value type - string */ + case DHCP_OPT_DOMAIN_NAME: + case DHCP_OPT_NIS_DOMAIN: + { + size_t len = strlen(option_data); + buf[0] = dhcp_option; + buf[1] = (uint8_t) len; + memcpy((void *)&buf[2], option_data, len); + ret_len = 2 + len; + } + } + + return ret_len; +} + +static void +compose_dhcp_options(struct dhcp_packet_ctx *ctx, char *ret, + uint32_t * ret_len) +{ + char *start = ret; + + /* Magic cookie */ + *(uint32_t *) ret = htonl(DHCP_MAGIC_COOKIE); + ret += (sizeof (uint32_t)); + + /* Dhcp option - type */ + ret[0] = (uint8_t) DHCP_OPT_MSG_TYPE; + ret[1] = (uint8_t) 1; + + if (ctx->message_type == DHCP_MSG_DISCOVER) { + /* DHCP DISCOVER. Set the dhcp message type as DHCP OFFER */ + ret[2] = (uint8_t)DHCP_MSG_OFFER; + } + else { + /* DHCP REQUEST, set the message type as DHCP ACK */ + ret[2] = (uint8_t) DHCP_MSG_ACK; + } + ret += 3; + + /* Dhcp server id*/ + ret[0] = (uint8_t)DHCP_OPT_SERVER_ID; + ret[1] = (uint8_t)4; + *((uint32_t *)&ret[2]) = htonl(DHCP_SERVER_ID); + ret += 6; + + int option_code; + struct smap_node *node; + int len; + bool mtu_set = false; + bool lease_period_set = false; + SMAP_FOR_EACH(node, &ctx->binding->options) { + if (strncmp(node->key, "dhcp-opt-", 9)) { + continue; + } + option_code = 0; + if(!ovs_scan(&node->key[9], "%d", &option_code)) { + continue; + } + if (!option_code) { + continue; + } + len = add_option_to_dhcp_resp_buf(ret, option_code, + node->value); + if (len) { + if (option_code == DHCP_OPT_IFACE_MTU) { + mtu_set = true; + }else if(option_code == DHCP_OPT_LEASE_TIME) { + lease_period_set = true; + } + ret += len; + } + } + + if (!lease_period_set) { + ret[0] = (uint8_t)DHCP_OPT_LEASE_TIME; + ret[1] = (uint8_t)4; + *((uint32_t *)&ret[2]) = htonl(DHCP_LEASE_PERIOD); + ret += 6; + } + + if (!mtu_set) { + /* Set the default MTU to 1400 to ensure that the the VM traffic + * after adding the tunnel headers + * do not exceed the MTU of underlying network + */ + ret[0] = (uint8_t)DHCP_OPT_IFACE_MTU; + ret[1] = (uint8_t)2; + *((uint16_t *)&ret[2]) = htons(DHCP_DEFAULT_IFACE_MTU); + ret += 4; + } + + /* Padding */ + *((uint32_t *) ret) = 0; + ret += 4; + + /* End */ + ret[0] = DHCP_OPT_END; + ret += 1; + + /* Padding */ + *((uint32_t *) ret) = 0; + ret += 4; + + *ret_len = (ret - start); +} + +static bool +is_dhcp_enabled_for_port(struct dhcp_packet_ctx *ctx) +{ + const struct sbrec_port_binding *binding; + struct eth_addr mac; + + ctx->binding = NULL; + SBREC_PORT_BINDING_FOR_EACH(binding, ctx->ctrl_ctx->ovnsb_idl) { + for (size_t i = 0; i < binding->n_mac; i++) { + if (!ovs_scan(binding->mac[i], + ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT, + ETH_ADDR_SCAN_ARGS(mac), + IP_SCAN_ARGS(&ctx->offered_ipv4))) { + continue; + } + if (eth_addr_to_uint64(mac) == + eth_addr_to_uint64(ctx->flow->dl_src)) { + ctx->binding = binding; + break; + } + } + if (ctx->binding) { + break; + } + } + if (!ctx->binding) { + return false; + } + + /* check if dhcp options - netmask and router are set */ + bool opt_netmask_present = false; + bool opt_router_present = false; + struct smap_node *node; + SMAP_FOR_EACH(node, &ctx->binding->options) { + if (!strcmp(node->key, DHCP_OPT_NETMASK_KEY)) { + opt_netmask_present = true; + } + else if(!strcmp(node->key, DHCP_OPT_ROUTER_KEY)) { + opt_router_present = true; + } + + if (opt_netmask_present && opt_router_present) { + return true; + } + } + + return false; +} + +static bool +compose_dhcp_response(struct dhcp_packet_ctx *ctx, + struct dhcp_header const *in_dhcp, + struct dp_packet *out_packet) +{ + /* TODO: Frame the proper eth_addr */ + struct eth_addr eth_addr = {.ea = {0x9a, 0x56, 0x02, 0x53, 0xc2, 0x40} }; + char options[1024]; + uint32_t options_length = 0; + + memset(options, 0, sizeof (options)); + + compose_dhcp_options(ctx, options, &options_length); + + size_t out_packet_length = ETH_HEADER_LEN + IP_HEADER_LEN + \ + UDP_HEADER_LEN + DHCP_HEADER_LEN + \ + options_length; + + dp_packet_init(out_packet, out_packet_length); + dp_packet_clear(out_packet); + dp_packet_prealloc_tailroom(out_packet, out_packet_length); + + struct eth_header *eth; + + eth = dp_packet_put_zeros(out_packet, sizeof (*eth)); + eth->eth_dst = ctx->flow->dl_src; + eth->eth_src = eth_addr; + eth->eth_type = ctx->flow->dl_type; + + struct ip_header *ip; + + ip = dp_packet_put_zeros(out_packet, sizeof (*ip)); + ip->ip_ihl_ver = IP_IHL_VER(5, 4); + ip->ip_tos = ctx->flow->nw_tos; + ip->ip_ttl = ctx->flow->nw_ttl; + ip->ip_proto = IPPROTO_UDP; + put_16aligned_be32(&ip->ip_src, (ovs_be32) 0x0); + put_16aligned_be32(&ip->ip_dst, ctx->offered_ipv4); + + struct udp_header *udp; + + udp = dp_packet_put_zeros(out_packet, sizeof (*udp)); + udp->udp_src = htons(ofp_to_u16(DHCP_SERVER_PORT)); + udp->udp_dst = htons(ofp_to_u16(DHCP_CLIENT_PORT)); + struct dhcp_header *dhcp; + + dhcp = dp_packet_put_zeros(out_packet, sizeof (*dhcp)); + memcpy(dhcp, in_dhcp, sizeof (struct dhcp_header)); + dhcp->op = DHCP_OP_REPLY; + dhcp->yiaddr = ctx->offered_ipv4; + + void *opts = dp_packet_put_zeros(out_packet, options_length); + + memcpy(opts, options, options_length); + + int udp_len = sizeof (*dhcp) + options_length + UDP_HEADER_LEN; + + udp->udp_len = htons(ofp_to_u16(udp_len)); + ip->ip_tot_len = htons(ofp_to_u16(IP_HEADER_LEN + udp_len)); + ip->ip_csum = csum(ip, sizeof *ip); + udp->udp_csum = 0; + return true; +} + +static struct ofpbuf * +process_dhcp_packet(struct dhcp_packet_ctx *ctx, + enum ofputil_protocol of_proto) +{ + struct dhcp_header const *dhcp_data = + dp_packet_get_udp_payload(ctx->packet); + struct dp_packet out; + struct ofputil_packet_out ofpacket_out; + struct ofpbuf ofpacts, *buf; + char const *footer = (char *) dhcp_data + sizeof (*dhcp_data); + uint32_t cookie = *(uint32_t *) footer; + + if (dhcp_data->op != DHCP_OP_REQUEST) { + return NULL; + } + if (cookie != htonl(DHCP_MAGIC_COOKIE)) { + /* Cookie validation failed */ + return NULL; + } + + footer += sizeof (uint32_t); + size_t dhcp_data_size = dp_packet_l4_size(ctx->packet); + + for (struct dhcp_option_header const *opt = + (struct dhcp_option_header *)footer; + footer < (char *) dhcp_data + dhcp_data_size; + footer += (sizeof (*opt) + opt->len)) { + opt = (struct dhcp_option_header *) footer; + switch (opt->option) { + case DHCP_OPT_MSG_TYPE: + { + ctx->message_type = *(uint8_t *) OPTION_PAYLOAD(opt); + if (ctx->message_type != DHCP_MSG_DISCOVER && + ctx->message_type != DHCP_MSG_REQUEST) { + return NULL; + } + break; + } + case DHCP_OPT_ADDR_REQ: + /* requested ip address */ + ctx->requested_ipv4 = *(ovs_be32 *) OPTION_PAYLOAD(opt); + break; + case DHCP_OPT_PARAMS: + /* XXX: Ignoring opt params from the client for now as + * we are sending the dhcp options set by the CMS for the port. + * May be this needs to be revisited. + */ + break; + } + + } + + ofpbuf_init(&ofpacts, 0); + ofpbuf_clear(&ofpacts); + + if (is_dhcp_enabled_for_port(ctx)) { + compose_dhcp_response(ctx, dhcp_data, &out); + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_IN_PORT; + ofpacket_out.packet = dp_packet_data(&out); + ofpacket_out.packet_len = dp_packet_size(&out); + } else { + /* ovn controller doesn't have enough information to handle the dhcp + * request. Flood the packet so that the dhcp server if running can + * respond + */ + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_FLOOD; + ofpacket_out.packet = dp_packet_data(ctx->packet); + ofpacket_out.packet_len = dp_packet_size(ctx->packet); + } + + ofpacket_out.buffer_id = UINT32_MAX; + ofpacket_out.in_port = ctx->pin->flow_metadata.flow.in_port.ofp_port; + ofpacket_out.ofpacts = ofpacts.data; + ofpacket_out.ofpacts_len = ofpacts.size; + buf = ofputil_encode_packet_out(&ofpacket_out, of_proto); + ofpbuf_uninit(&ofpacts); + return buf; +} + +bool +ovn_dhcp_process_packet(struct controller_ctx * ctx, + struct ofputil_packet_in * pin, + enum ofputil_protocol ofp_proto, + struct flow *flow, + struct dp_packet *packet, + struct ofpbuf **ret_buf) +{ + struct dhcp_packet_ctx dhcp_ctx = { + .ctrl_ctx = ctx, + .pin = pin, + .flow = flow, + .packet = packet, + }; + *ret_buf = process_dhcp_packet(&dhcp_ctx, ofp_proto); + return (*ret_buf != NULL); +} diff --git a/ovn/controller/ovn-dhcp.h b/ovn/controller/ovn-dhcp.h new file mode 100644 index 0000000..9d8c500 --- /dev/null +++ b/ovn/controller/ovn-dhcp.h @@ -0,0 +1,34 @@ + +/* Copyright (c) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_DHCP_H +#define OVN_DHCP_H + +#include "ofp-util.h" + +struct controller_ctx; +struct ofpbuf; +struct flow; +struct dp_packet; + +bool ovn_dhcp_process_packet(struct controller_ctx * ctx, + struct ofputil_packet_in * pin, + enum ofputil_protocol ofp_proto, + struct flow *flow, + struct dp_packet *packet, + struct ofpbuf **ret_buf); + +#endif diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 9e7caf4..73d36b6 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -16,6 +16,7 @@ #include #include "dirs.h" +#include "dp-packet.h" #include "pinctrl.h" #include "ofp-msgs.h" #include "ofp-print.h" @@ -24,9 +25,13 @@ #include "openvswitch/vlog.h" #include "socket-util.h" #include "vswitch-idl.h" +#include "ovn-dhcp.h" VLOG_DEFINE_THIS_MODULE(pinctrl); +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + /* OpenFlow connection to the switch. */ static struct rconn *swconn; @@ -73,11 +78,35 @@ set_switch_config(struct rconn *swconn, const struct ofp_switch_config *config) queue_msg(request); } +static enum ofputil_protocol +pinctrl_ofp_proto(void) +{ + enum ofp_version version; + + version = rconn_get_version(swconn); + return ofputil_protocol_from_ofp_version(version); +} + +static inline bool +is_dhcp_packet(struct flow *flow) +{ + if (flow->dl_type == htons(ETH_TYPE_IP) && + flow->nw_proto == IPPROTO_UDP && + flow->nw_src == INADDR_ANY && + flow->nw_dst == INADDR_BROADCAST && + flow->tp_src == htons(DHCP_CLIENT_PORT) && + flow->tp_dst == htons(DHCP_SERVER_PORT)) { + return true; + } + return false; +} + static void process_packet_in(struct controller_ctx *ctx OVS_UNUSED, const struct ofp_header *msg) { struct ofputil_packet_in pin; + struct ofpbuf *buf = NULL; if (ofputil_decode_packet_in(&pin, msg) != 0) { return; @@ -86,7 +115,19 @@ process_packet_in(struct controller_ctx *ctx OVS_UNUSED, return; } - /* XXX : process the received packet */ + struct flow flow; + struct dp_packet packet; + dp_packet_use_const(&packet, pin.packet, pin.packet_len); + flow_extract(&packet, &flow); + + if (is_dhcp_packet(&flow)) { + ovn_dhcp_process_packet(ctx, &pin, pinctrl_ofp_proto(), + &flow, &packet, &buf); + if (buf) { + rconn_send(swconn, buf, NULL); + } + } + } static void diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index b6eef03..2aae07e 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -181,6 +181,35 @@ Required. A logical switch name connected by the VTEP gateway. + + +

+ These options apply when is + (empty string). These options are used by the + ovn-controller to reply to the dhcp requests from the + vm ports. + The format of the key should be "dhcp-opt-OPTCODE" where OPTCODE is + the dhcp option code. + Eg. To set netmask dhcp option, the key should be "dhcp-opt-1". + ovn-controller will reply to the dhcp packet only + if dhcp option netmask - "dhcp-opt-1" and dhcp option router + "dhcp-opt-3" are set. + ovn-controller expects the CMS plugin to set the values + of the dhcp options as defined in the RFC 2132. +

+ + + Optional. Dhcp option netmask value to be returned by the + ovn-controller to the DHCP discover/request + from the vm port. + + + + Optional. Dhcp option router value to be returned by the + ovn-controller to the DHCP discover/request + from the vm port. + +
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index e674f3a..03f96fc 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1300,6 +1300,35 @@ tcp.flags = RST; + +

+ These options apply when is + (empty string). These options are used by the + ovn-controller to reply to the dhcp requests from the + vm ports. + The format of the key should be "dhcp-opt-OPTCODE" where OPTCODE is + the dhcp option code. + Eg. To set netmask dhcp option, the key should be "dhcp-opt-1". + ovn-controller will reply to the dhcp packet only + if dhcp option netmask - "dhcp-opt-1" and dhcp option router + "dhcp-opt-3" are set. + ovn-controller expects the CMS plugin to set the values + of the dhcp options as defined in the RFC 2132. +

+ + + Optional. Dhcp option netmask value to be returned by the + ovn-controller to the DHCP discover/request + from the vm port. + + + + Optional. Dhcp option router value to be returned by the + ovn-controller to the DHCP discover/request + from the vm port. + +
+

These columns support containers nested within a VM. Specifically,