kfree_skb(skb);
return 0;
}
- if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP) &&
- p->flags & (compare_ether_addr(key.dl_dst, stp_eth_addr)
- ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) {
+ if (p && p->config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
+ p->config & (compare_ether_addr(key.dl_dst, stp_eth_addr)
+ ? OFPPC_NO_RECV : OFPPC_NO_RECV_STP)) {
kfree_skb(skb);
return 0;
}
flow = chain_lookup(chain, &key);
if (likely(flow != NULL)) {
+ struct sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
flow_used(flow, skb);
execute_actions(chain->dp, skb, &key,
- flow->actions, flow->n_actions, 0);
+ sf_acts->actions, sf_acts->n_actions, 0);
return 0;
} else {
return -ESRCH;
}
void execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct sw_flow_key *key,
+ struct sw_flow_key *key,
const struct ofp_action *actions, int n_actions,
int ignore_no_fwd)
{
return skb;
}
-static struct sk_buff *modify_vlan(struct sk_buff *skb,
- const struct sw_flow_key *key, const struct ofp_action *a)
+static struct sk_buff *modify_vlan_tci(struct sk_buff *skb,
+ struct sw_flow_key *key, uint16_t tci, uint16_t mask)
{
- uint16_t new_id = ntohs(a->arg.vlan_id);
-
- if (new_id != OFP_VLAN_NONE) {
- if (key->dl_vlan != htons(OFP_VLAN_NONE)) {
- /* Modify vlan id, but maintain other TCI values */
- struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
- vh->h_vlan_TCI = (vh->h_vlan_TCI
- & ~(htons(VLAN_VID_MASK))) | a->arg.vlan_id;
- } else {
- /* Add vlan header */
-
- /* xxx The vlan_put_tag function, doesn't seem to work
- * xxx reliably when it attempts to use the hardware-accelerated
- * xxx version. We'll directly use the software version
- * xxx until the problem can be diagnosed.
- */
- skb = __vlan_put_tag(skb, new_id);
- }
+ struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
+
+ if (key->dl_vlan != htons(OFP_VLAN_NONE)) {
+ /* Modify vlan id, but maintain other TCI values */
+ vh->h_vlan_TCI = (vh->h_vlan_TCI & ~(htons(mask))) | htons(tci);
} else {
- /* Remove an existing vlan header if it exists */
- vlan_pull_tag(skb);
+ /* Add vlan header */
+
+ /* xxx The vlan_put_tag function, doesn't seem to work
+ * xxx reliably when it attempts to use the hardware-accelerated
+ * xxx version. We'll directly use the software version
+ * xxx until the problem can be diagnosed.
+ */
+ skb = __vlan_put_tag(skb, tci);
+ vh = vlan_eth_hdr(skb);
}
+ key->dl_vlan = vh->h_vlan_TCI & htons(VLAN_VID_MASK);
return skb;
}
+/* Mask for the priority bits in a vlan header. The kernel doesn't
+ * define this like it does for VID. */
+#define VLAN_PCP_MASK 0xe000
+
struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto,
- const struct sw_flow_key *key, const struct ofp_action *a)
+ struct sw_flow_key *key, const struct ofp_action *a)
{
switch (ntohs(a->type)) {
- case OFPAT_SET_DL_VLAN:
- skb = modify_vlan(skb, key, a);
+ case OFPAT_SET_VLAN_VID: {
+ uint16_t tci = ntohs(a->arg.vlan_vid);
+ skb = modify_vlan_tci(skb, key, tci, VLAN_VID_MASK);
+ break;
+ }
+
+ case OFPAT_SET_VLAN_PCP: {
+ uint16_t tci = (uint16_t)a->arg.vlan_pcp << 13;
+ skb = modify_vlan_tci(skb, key, tci, VLAN_PCP_MASK);
+ break;
+ }
+
+ case OFPAT_STRIP_VLAN:
+ vlan_pull_tag(skb);
+ key->dl_vlan = htons(OFP_VLAN_NONE);
break;
case OFPAT_SET_DL_SRC: {
return skb;
}
+static int
+recv_hello(struct sw_chain *chain, const struct sender *sender,
+ const void *msg)
+{
+ return dp_send_hello(chain->dp, sender, msg);
+}
+
static int
recv_features_request(struct sw_chain *chain, const struct sender *sender,
const void *msg)
flow->idle_timeout = ntohs(ofm->idle_timeout);
flow->hard_timeout = ntohs(ofm->hard_timeout);
flow->used = jiffies;
- flow->n_actions = n_actions;
flow->init_time = jiffies;
flow->byte_count = 0;
flow->packet_count = 0;
spin_lock_init(&flow->lock);
- memcpy(flow->actions, ofm->actions, n_actions * sizeof *flow->actions);
+ memcpy(flow->sf_acts->actions, ofm->actions,
+ n_actions * sizeof *flow->sf_acts->actions);
/* Act. */
error = chain_insert(chain, flow);
return error;
}
+static int
+mod_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
+{
+ int error = -ENOMEM;
+ int i;
+ int n_actions;
+ struct sw_flow_key key;
+ uint16_t priority;
+ int strict;
+
+ /* To prevent loops, make sure there's no action to send to the
+ * OFP_TABLE virtual port.
+ */
+ n_actions = (ntohs(ofm->header.length) - sizeof *ofm)
+ / sizeof *ofm->actions;
+ for (i=0; i<n_actions; i++) {
+ const struct ofp_action *a = &ofm->actions[i];
+
+ if (a->type == htons(OFPAT_OUTPUT)
+ && (a->arg.output.port == htons(OFPP_TABLE)
+ || a->arg.output.port == htons(OFPP_NONE)
+ || a->arg.output.port == ofm->match.in_port)) {
+ /* xxx Send fancy new error message? */
+ goto error;
+ }
+ }
+
+ flow_extract_match(&key, &ofm->match);
+ priority = key.wildcards ? ntohs(ofm->priority) : -1;
+ strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
+ chain_modify(chain, &key, priority, strict, ofm->actions, n_actions);
+
+ if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
+ struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
+ if (skb) {
+ struct sw_flow_key skb_key;
+ flow_extract(skb, ntohs(ofm->match.in_port), &skb_key);
+ execute_actions(chain->dp, skb, &skb_key,
+ ofm->actions, n_actions, 0);
+ }
+ else
+ error = -ESRCH;
+ }
+ return error;
+
+error:
+ if (ntohl(ofm->buffer_id) != (uint32_t) -1)
+ discard_skb(ntohl(ofm->buffer_id));
+ return error;
+}
+
static int
recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
{
if (command == OFPFC_ADD) {
return add_flow(chain, ofm);
+ } else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) {
+ return mod_flow(chain, ofm);
} else if (command == OFPFC_DELETE) {
struct sw_flow_key key;
flow_extract_match(&key, &ofm->match);
};
static const struct openflow_packet packets[] = {
+ [OFPT_HELLO] = {
+ sizeof (struct ofp_header),
+ recv_hello,
+ },
[OFPT_FEATURES_REQUEST] = {
sizeof (struct ofp_header),
recv_features_request,
},
};
- const struct openflow_packet *pkt;
struct ofp_header *oh;
oh = (struct ofp_header *) msg;
- if (oh->version != OFP_VERSION || oh->type >= ARRAY_SIZE(packets)
- || ntohs(oh->length) > length)
+ if (oh->version != OFP_VERSION
+ && oh->type != OFPT_HELLO
+ && oh->type != OFPT_ERROR
+ && oh->type != OFPT_ECHO_REQUEST
+ && oh->type != OFPT_ECHO_REPLY
+ && oh->type != OFPT_VENDOR)
+ {
+ dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST,
+ OFPBRC_BAD_VERSION, msg, length);
+ return -EINVAL;
+ }
+ if (ntohs(oh->length) > length)
return -EINVAL;
- pkt = &packets[oh->type];
- if (!pkt->handler)
- return -ENOSYS;
- if (length < pkt->min_size)
- return -EFAULT;
-
- return pkt->handler(chain, sender, msg);
+ if (oh->type < ARRAY_SIZE(packets)) {
+ const struct openflow_packet *pkt = &packets[oh->type];
+ if (pkt->handler) {
+ if (length < pkt->min_size)
+ return -EFAULT;
+ return pkt->handler(chain, sender, msg);
+ }
+ }
+ dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST,
+ OFPBRC_BAD_TYPE, msg, length);
+ return -EINVAL;
}
/* Packet buffering. */