Skip to content
Permalink
Browse files

ovn-controller: Add 'put_dhcp_opts' action in ovn-controller

This patch adds a new OVN action 'put_dhcp_opts' to support native
DHCP in OVN.

ovn-controller parses this action and adds a NXT_PACKET_IN2
OF flow with 'pause' flag set and the DHCP options stored in
'userdata' field.

When the valid DHCP packet is received by ovn-controller, it frames a
new DHCP reply packet with the DHCP options present in the
'userdata' field and resumes the packet and stores 1 in the 1-bit subfield.
If the packet is invalid, it resumes the packet without any modifying and
stores 0 in the 1-bit subfield.

Eg. reg0[0] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1,
                  netmask = 255.255.255.0, lease_time = 3600,....)

A new 'DHCP_Options' table is added in SB DB which stores
the supported DHCP options with DHCP code and type. ovn-northd is
expected to popule this table.

The next patch will add logical flows with this action.

Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
Co-authored-by: Ben Pfaff <blp@ovn.org>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information...
numansiddique and blp committed Jun 15, 2016
1 parent 8fb72d2 commit 42814145d70c77462ce28b38841cd160f0486776
Showing with 864 additions and 46 deletions.
  1. +12 −0 include/openvswitch/meta-flow.h
  2. +13 −0 lib/dhcp.h
  3. +11 −0 ovn/controller/lflow.c
  4. +187 −1 ovn/controller/pinctrl.c
  5. +199 −7 ovn/lib/actions.c
  6. +13 −0 ovn/lib/actions.h
  7. +1 −0 ovn/lib/automake.mk
  8. +10 −35 ovn/lib/expr.c
  9. +38 −0 ovn/lib/expr.h
  10. +111 −0 ovn/lib/ovn-dhcp.h
  11. +14 −2 ovn/ovn-sb.ovsschema
  12. +198 −0 ovn/ovn-sb.xml
  13. +25 −1 tests/ovn.at
  14. +32 −0 tests/test-ovn.c
@@ -1929,6 +1929,18 @@ union mf_subvalue {
ovs_be64 be64[16];

/* Convenient access to just least-significant bits in various forms. */
struct {
uint8_t dummy_u8[127];
uint8_t u8_val;
};
struct {
ovs_be16 dummy_be16[63];
ovs_be16 be16_int;
};
struct {
ovs_be32 dummy_be32[31];
ovs_be32 be32_int;
};
struct {
ovs_be64 dummy_integer[15];
ovs_be64 integer;
@@ -25,6 +25,8 @@
#define DHCP_SERVER_PORT 67 /* Port used by DHCP server. */
#define DHCP_CLIENT_PORT 68 /* Port used by DHCP client. */

#define DHCP_MAGIC_COOKIE 0x63825363

#define DHCP_HEADER_LEN 236
struct dhcp_header {
uint8_t op; /* DHCP_BOOTREQUEST or DHCP_BOOTREPLY. */
@@ -45,4 +47,15 @@ struct dhcp_header {
};
BUILD_ASSERT_DECL(DHCP_HEADER_LEN == sizeof(struct dhcp_header));

#define DHCP_OP_REQUEST 1
#define DHCP_OP_REPLY 2

#define DHCP_MSG_DISCOVER 1
#define DHCP_MSG_OFFER 2
#define DHCP_MSG_REQUEST 3
#define DHCP_MSG_ACK 5

#define DHCP_OPT_MSG_TYPE 53
#define DHCP_OPT_END 255

#endif /* dhcp.h */
@@ -24,6 +24,7 @@
#include "ovn-controller.h"
#include "ovn/lib/actions.h"
#include "ovn/lib/expr.h"
#include "ovn/lib/ovn-dhcp.h"
#include "ovn/lib/ovn-sb-idl.h"
#include "packets.h"
#include "simap.h"
@@ -203,6 +204,13 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
{
uint32_t conj_id_ofs = 1;

struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
const struct sbrec_dhcp_options *dhcp_opt_row;
SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) {
dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
dhcp_opt_row->type);
}

const struct sbrec_logical_flow *lflow;
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
/* Determine translation of logical table IDs to physical table IDs. */
@@ -274,6 +282,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
};
struct action_params ap = {
.symtab = &symtab,
.dhcp_opts = &dhcp_opts,
.lookup_port = lookup_port_cb,
.aux = &aux,
.ct_zones = ct_zones,
@@ -357,6 +366,8 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
ofpbuf_uninit(&ofpacts);
conj_id_ofs += n_conjs;
}

dhcp_opts_destroy(&dhcp_opts);
}

static void
@@ -18,17 +18,21 @@
#include "pinctrl.h"

#include "coverage.h"
#include "csum.h"
#include "dirs.h"
#include "dp-packet.h"
#include "flow.h"
#include "lport.h"
#include "nx-match.h"
#include "ovn-controller.h"
#include "lib/sset.h"
#include "openvswitch/ofp-actions.h"
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-util.h"
#include "openvswitch/vlog.h"

#include "lib/dhcp.h"
#include "ovn-controller.h"
#include "ovn/lib/actions.h"
#include "ovn/lib/logical-fields.h"
@@ -203,14 +207,192 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
ofpbuf_uninit(&ofpacts);
}

static void
pinctrl_handle_put_dhcp_opts(
struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
struct ofpbuf *userdata, struct ofpbuf *continuation)
{
enum ofp_version version = rconn_get_version(swconn);
enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
struct dp_packet *pkt_out_ptr = NULL;
uint32_t success = 0;

/* Parse result field. */
const struct mf_field *f;
enum ofperr ofperr = nx_pull_header(userdata, &f, NULL);
if (ofperr) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
goto exit;
}

/* Parse result offset and offer IP. */
ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
ovs_be32 *offer_ip = ofpbuf_try_pull(userdata, sizeof *offer_ip);
if (!ofsp || !offer_ip) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "offset or offer_ip not present in the userdata");
goto exit;
}

/* Check that the result is valid and writable. */
struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 };
ofperr = mf_check_dst(&dst, NULL);
if (ofperr) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr));
goto exit;
}

if (!userdata->size) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "DHCP options not present in the userdata");
goto exit;
}

/* Validate the DHCP request packet.
* Format of the DHCP packet is
* ------------------------------------------------------------------------
*| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var len)|
* ------------------------------------------------------------------------
*/
if (dp_packet_l4_size(pkt_in) < (UDP_HEADER_LEN +
sizeof (struct dhcp_header) + sizeof(uint32_t) + 3)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet recieved");
goto exit;
}

struct dhcp_header const *in_dhcp_data = dp_packet_get_udp_payload(pkt_in);
if (in_dhcp_data->op != DHCP_OP_REQUEST) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Invalid opcode in the DHCP packet : %d",
in_dhcp_data->op);
goto exit;
}

/* DHCP options follow the DHCP header. The first 4 bytes of the DHCP
* options is the DHCP magic cookie followed by the actual DHCP options.
*/
const uint8_t *in_dhcp_opt =
(const uint8_t *)dp_packet_get_udp_payload(pkt_in) +
sizeof (struct dhcp_header);

ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE);
if (memcmp(in_dhcp_opt, &magic_cookie, sizeof(ovs_be32))) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "DHCP magic cookie not present in the DHCP packet");
goto exit;
}

in_dhcp_opt += 4;
/* Check that the DHCP Message Type (opt 53) is present or not with
* valid values - DHCP_MSG_DISCOVER or DHCP_MSG_REQUEST as the first
* DHCP option.
*/
if (!(in_dhcp_opt[0] == DHCP_OPT_MSG_TYPE && in_dhcp_opt[1] == 1 && (
in_dhcp_opt[2] == DHCP_MSG_DISCOVER ||
in_dhcp_opt[2] == DHCP_MSG_REQUEST))) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Invalid DHCP message type : opt code = %d,"
" opt value = %d", in_dhcp_opt[0], in_dhcp_opt[2]);
goto exit;
}

uint8_t msg_type;
if (in_dhcp_opt[2] == DHCP_MSG_DISCOVER) {
msg_type = DHCP_MSG_OFFER;
} else {
msg_type = DHCP_MSG_ACK;
}

/* Frame the DHCP reply packet
* Total DHCP options length will be options stored in the userdata +
* 16 bytes.
*
* --------------------------------------------------------------
*| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options |
* --------------------------------------------------------------
*| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding|
* --------------------------------------------------------------
*/
uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + \
userdata->size + 16;
size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;

struct dp_packet pkt_out;
dp_packet_init(&pkt_out, new_packet_size);
dp_packet_clear(&pkt_out);
dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
pkt_out_ptr = &pkt_out;

/* Copy the L2 and L3 headers from the pkt_in as they would remain same*/
dp_packet_put(
&pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs);

pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
pkt_out.l2_pad_size = pkt_in->l2_pad_size;
pkt_out.l3_ofs = pkt_in->l3_ofs;
pkt_out.l4_ofs = pkt_in->l4_ofs;

struct udp_header *udp = dp_packet_put(
&pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);

struct dhcp_header *dhcp_data = dp_packet_put(
&pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN);
dhcp_data->op = DHCP_OP_REPLY;
dhcp_data->yiaddr = *offer_ip;
dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32));

uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out,
userdata->size + 12);
/* DHCP option - type */
out_dhcp_opts[0] = DHCP_OPT_MSG_TYPE;
out_dhcp_opts[1] = 1;
out_dhcp_opts[2] = msg_type;
out_dhcp_opts += 3;

memcpy(out_dhcp_opts, userdata->data, userdata->size);
out_dhcp_opts += userdata->size;
/* Padding */
out_dhcp_opts += 4;
/* End */
out_dhcp_opts[0] = DHCP_OPT_END;

udp->udp_len = htons(new_l4_size);

struct ip_header *out_ip = dp_packet_l3(&pkt_out);
out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
udp->udp_csum = 0;
out_ip->ip_csum = 0;
out_ip->ip_csum = csum(out_ip, sizeof *out_ip);

pin->packet = dp_packet_data(&pkt_out);
pin->packet_len = dp_packet_size(&pkt_out);

success = 1;
exit:
if (!ofperr) {
union mf_subvalue sv;
sv.u8_val = success;
mf_write_subfield(&dst, &sv, &pin->flow_metadata);
}
queue_msg(ofputil_encode_resume(pin, continuation, proto));
if (pkt_out_ptr) {
dp_packet_uninit(pkt_out_ptr);
}
}

static void
process_packet_in(const struct ofp_header *msg)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);

struct ofputil_packet_in pin;
struct ofpbuf continuation;
enum ofperr error = ofputil_decode_packet_in(msg, true, &pin,
NULL, NULL, NULL);
NULL, NULL, &continuation);

if (error) {
VLOG_WARN_RL(&rl, "error decoding packet-in: %s",
ofperr_to_string(error));
@@ -242,6 +424,10 @@ process_packet_in(const struct ofp_header *msg)
pinctrl_handle_put_arp(&pin.flow_metadata.flow, &headers);
break;

case ACTION_OPCODE_PUT_DHCP_OPTS:
pinctrl_handle_put_dhcp_opts(&packet, &pin, &userdata, &continuation);
break;

default:
VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
ntohl(ah->opcode));

0 comments on commit 4281414

Please sign in to comment.
You can’t perform that action at this time.