X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=datapath%2Fforward.c;h=4dee840b48e03ad1f27cd5673b302b199e4f06cc;hb=eca2aeb329f1ebf08cbda14f08197456d903111b;hp=a2b6691f1bff8f6be13eee9d46c7ffea6784f749;hpb=60a87c9b0032346568485ad40fd72ea1f72b8674;p=sliver-openvswitch.git diff --git a/datapath/forward.c b/datapath/forward.c index a2b6691f1..4dee840b4 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -21,32 +21,48 @@ /* FIXME: do we need to use GFP_ATOMIC everywhere here? */ -static void execute_actions(struct datapath *, struct sk_buff *, - const struct sw_flow_key *, - const struct ofp_action *, int n_actions); static int make_writable(struct sk_buff **); static struct sk_buff *retrieve_skb(uint32_t id); static void discard_skb(uint32_t id); /* 'skb' was received on 'in_port', a physical switch port between 0 and - * OFPP_MAX. Process it according to 'chain'. */ -void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb, int in_port) + * OFPP_MAX. Process it according to 'chain'. Returns 0 if successful, in + * which case 'skb' is destroyed, or -ESRCH if there is no matching flow, in + * which case 'skb' still belongs to the caller. */ +int run_flow_through_tables(struct sw_chain *chain, struct sk_buff *skb, + int in_port) { struct sw_flow_key key; struct sw_flow *flow; - flow_extract(skb, in_port, &key); + if (flow_extract(skb, in_port, &key) + && (chain->dp->flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) { + /* Drop fragment. */ + kfree_skb(skb); + return 0; + } + flow = chain_lookup(chain, &key); if (likely(flow != NULL)) { flow_used(flow, skb); execute_actions(chain->dp, skb, &key, flow->actions, flow->n_actions); + return 0; } else { + return -ESRCH; + } +} + +/* 'skb' was received on 'in_port', a physical switch port between 0 and + * OFPP_MAX. Process it according to 'chain', sending it up to the controller + * if no flow matches. Takes ownership of 'skb'. */ +void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb, int in_port) +{ + if (run_flow_through_tables(chain, skb, in_port)) dp_output_control(chain->dp, skb, fwd_save_skb(skb), - chain->dp->config.miss_send_len, + chain->dp->miss_send_len, OFPR_NO_MATCH); - } } static int do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len, @@ -60,7 +76,7 @@ static int do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len, max_len, OFPR_ACTION)); } -static void execute_actions(struct datapath *dp, struct sk_buff *skb, +void execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key, const struct ofp_action *actions, int n_actions) { @@ -90,10 +106,16 @@ static void execute_actions(struct datapath *dp, struct sk_buff *skb, max_len = ntohs(a->arg.output.max_len); } else { if (!make_writable(&skb)) { - printk("make_writable failed\n"); + if (net_ratelimit()) + printk("make_writable failed\n"); break; } skb = execute_setter(skb, eth_proto, key, a); + if (!skb) { + if (net_ratelimit()) + printk("execute_setter lost skb\n"); + return; + } } } if (prev_port != -1) @@ -202,17 +224,23 @@ static struct sk_buff *vlan_pull_tag(struct sk_buff *skb) static struct sk_buff *modify_vlan(struct sk_buff *skb, const struct sw_flow_key *key, const struct ofp_action *a) { - uint16_t new_id = a->arg.vlan_id; + 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))) | htons(new_id); + & ~(htons(VLAN_VID_MASK))) | a->arg.vlan_id; } else { /* Add vlan header */ - skb = vlan_put_tag(skb, new_id); + + /* 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); } } else { /* Remove an existing vlan header if it exists */ @@ -260,29 +288,34 @@ struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto, } static int -recv_features_request(struct sw_chain *chain, const void *msg) +recv_features_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) { - const struct ofp_header *ofr = msg; - return dp_send_features_reply(chain->dp, ofr->xid); + return dp_send_features_reply(chain->dp, sender); } static int -recv_get_config_request(struct sw_chain *chain, const void *msg) +recv_get_config_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) { - const struct ofp_header *ofr = msg; - return dp_send_config_reply(chain->dp, ofr->xid); + return dp_send_config_reply(chain->dp, sender); } static int -recv_set_config(struct sw_chain *chain, const void *msg) +recv_set_config(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_switch_config *osc = msg; - chain->dp->config = *osc; + + chain->dp->flags = ntohs(osc->flags); + chain->dp->miss_send_len = ntohs(osc->miss_send_len); + return 0; } static int -recv_packet_out(struct sw_chain *chain, const void *msg) +recv_packet_out(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_packet_out *opo = msg; struct sk_buff *skb; @@ -330,7 +363,8 @@ recv_packet_out(struct sw_chain *chain, const void *msg) } static int -recv_port_mod(struct sw_chain *chain, const void *msg) +recv_port_mod(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_port_mod *opm = msg; @@ -339,19 +373,42 @@ recv_port_mod(struct sw_chain *chain, const void *msg) return 0; } +static int +recv_echo_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + return dp_send_echo_reply(chain->dp, sender, msg); +} + +static int +recv_echo_reply(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + return 0; +} + static int add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm) { int error = -ENOMEM; + int i; int n_acts; struct sw_flow *flow; - /* Check number of actions. */ + /* To prevent loops, make sure there's no action to send to the + * OFP_TABLE virtual port. + */ n_acts = (ntohs(ofm->header.length) - sizeof *ofm) / sizeof *ofm->actions; - if (n_acts > MAX_ACTIONS) { - error = -E2BIG; - goto error; + for (i=0; iactions[i]; + + if (a->type == htons(OFPAT_OUTPUT) + && (a->arg.output.port == htons(OFPP_TABLE) + || a->arg.output.port == htons(OFPP_NONE))) { + /* xxx Send fancy new error message? */ + goto error; + } } /* Allocate memory. */ @@ -361,14 +418,13 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm) /* Fill out flow. */ flow_extract_match(&flow->key, &ofm->match); - flow->group_id = ntohl(ofm->group_id); flow->max_idle = ntohs(ofm->max_idle); + flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1; flow->timeout = jiffies + flow->max_idle * HZ; flow->n_actions = n_acts; flow->init_time = jiffies; flow->byte_count = 0; flow->packet_count = 0; - atomic_set(&flow->deleted, 0); spin_lock_init(&flow->lock); memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions); @@ -400,7 +456,7 @@ error: } static int -recv_flow(struct sw_chain *chain, const void *msg) +recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg) { const struct ofp_flow_mod *ofm = msg; uint16_t command = ntohs(ofm->command); @@ -410,25 +466,29 @@ recv_flow(struct sw_chain *chain, const void *msg) } else if (command == OFPFC_DELETE) { struct sw_flow_key key; flow_extract_match(&key, &ofm->match); - return chain_delete(chain, &key, 0) ? 0 : -ESRCH; + return chain_delete(chain, &key, 0, 0) ? 0 : -ESRCH; } else if (command == OFPFC_DELETE_STRICT) { struct sw_flow_key key; + uint16_t priority; flow_extract_match(&key, &ofm->match); - return chain_delete(chain, &key, 1) ? 0 : -ESRCH; + priority = key.wildcards ? ntohs(ofm->priority) : -1; + return chain_delete(chain, &key, priority, 1) ? 0 : -ESRCH; } else { return -ENOTSUPP; } } -/* 'msg', which is 'length' bytes long, was received from the control path. - * Apply it to 'chain'. */ +/* 'msg', which is 'length' bytes long, was received across Netlink from + * 'sender'. Apply it to 'chain'. */ int -fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) +fwd_control_input(struct sw_chain *chain, const struct sender *sender, + const void *msg, size_t length) { struct openflow_packet { size_t min_size; - int (*handler)(struct sw_chain *, const void *); + int (*handler)(struct sw_chain *, const struct sender *, + const void *); }; static const struct openflow_packet packets[] = { @@ -456,16 +516,21 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) sizeof (struct ofp_port_mod), recv_port_mod, }, + [OFPT_ECHO_REQUEST] = { + sizeof (struct ofp_header), + recv_echo_request, + }, + [OFPT_ECHO_REPLY] = { + sizeof (struct ofp_header), + recv_echo_reply, + }, }; const struct openflow_packet *pkt; struct ofp_header *oh; - if (length < sizeof(struct ofp_header)) - return -EINVAL; - oh = (struct ofp_header *) msg; - if (oh->version != 1 || oh->type >= ARRAY_SIZE(packets) + if (oh->version != OFP_VERSION || oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length) return -EINVAL; @@ -475,7 +540,7 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) if (length < pkt->min_size) return -EFAULT; - return pkt->handler(chain, msg); + return pkt->handler(chain, sender, msg); } /* Packet buffering. */ @@ -544,6 +609,19 @@ static struct sk_buff *retrieve_skb(uint32_t id) return skb; } +void fwd_discard_all(void) +{ + unsigned long int flags; + int i; + + spin_lock_irqsave(&buffer_lock, flags); + for (i = 0; i < N_PKT_BUFFERS; i++) { + kfree_skb(buffers[i].skb); + buffers[i].skb = NULL; + } + spin_unlock_irqrestore(&buffer_lock, flags); +} + static void discard_skb(uint32_t id) { unsigned long int flags; @@ -560,10 +638,7 @@ static void discard_skb(uint32_t id) void fwd_exit(void) { - int i; - - for (i = 0; i < N_PKT_BUFFERS; i++) - kfree_skb(buffers[i].skb); + fwd_discard_all(); } /* Utility functions. */ @@ -581,7 +656,7 @@ make_writable(struct sk_buff **pskb) if (skb_shared(*pskb) || skb_cloned(*pskb)) goto copy_skb; - return pskb_may_pull(*pskb, 64); /* FIXME? */ + return pskb_may_pull(*pskb, 40); /* FIXME? */ copy_skb: nskb = skb_copy(*pskb, GFP_ATOMIC);