X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdpif-netdev.c;h=eb8d66d9cae4e10e5ec2f47e5b2091c4beb5c11a;hb=fea393b1d6b2729a784b898dbdd48d30d42e3ff7;hp=af62bf0c25e6d6405a7fd6bb8fb439d4b626acc0;hpb=a8d9304d121f56af0e6f02677342933579be3277;p=sliver-openvswitch.git diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index af62bf0c2..eb8d66d9c 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -47,6 +47,7 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "random.h" #include "shash.h" #include "timeval.h" #include "util.h" @@ -80,12 +81,10 @@ struct dp_netdev { int open_cnt; bool destroyed; - bool drop_frags; /* Drop all IP fragments, if true. */ struct dp_netdev_queue queues[N_QUEUES]; struct hmap flow_table; /* Flow table. */ /* Statistics. */ - long long int n_frags; /* Number of dropped IP fragments. */ long long int n_hit; /* Number of flow table matches. */ long long int n_missed; /* Number of flow table misses. */ long long int n_lost; /* Number of misses not passed to client. */ @@ -148,10 +147,10 @@ static int dpif_netdev_open(const struct dpif_class *, const char *name, static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *, int queue_no, const struct flow *, uint64_t arg); -static int dp_netdev_execute_actions(struct dp_netdev *, - struct ofpbuf *, struct flow *, - const struct nlattr *actions, - size_t actions_len); +static void dp_netdev_execute_actions(struct dp_netdev *, + struct ofpbuf *, struct flow *, + const struct nlattr *actions, + size_t actions_len); static struct dpif_class dpif_dummy_class; @@ -197,7 +196,6 @@ create_dp_netdev(const char *name, const struct dpif_class *class, dp->class = class; dp->name = xstrdup(name); dp->open_cnt = 0; - dp->drop_frags = false; for (i = 0; i < N_QUEUES; i++) { dp->queues[i].head = dp->queues[i].tail = 0; } @@ -301,29 +299,12 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats) { struct dp_netdev *dp = get_dp_netdev(dpif); stats->n_flows = hmap_count(&dp->flow_table); - stats->n_frags = dp->n_frags; stats->n_hit = dp->n_hit; stats->n_missed = dp->n_missed; stats->n_lost = dp->n_lost; return 0; } -static int -dpif_netdev_get_drop_frags(const struct dpif *dpif, bool *drop_fragsp) -{ - struct dp_netdev *dp = get_dp_netdev(dpif); - *drop_fragsp = dp->drop_frags; - return 0; -} - -static int -dpif_netdev_set_drop_frags(struct dpif *dpif, bool drop_frags) -{ - struct dp_netdev *dp = get_dp_netdev(dpif); - dp->drop_frags = drop_frags; - return 0; -} - static int do_add_port(struct dp_netdev *dp, const char *devname, const char *type, uint16_t port_no) @@ -691,78 +672,10 @@ dpif_netdev_flow_get(const struct dpif *dpif, return 0; } -static int -dpif_netdev_validate_actions(const struct nlattr *actions, - size_t actions_len, bool *mutates) -{ - const struct nlattr *a; - unsigned int left; - - *mutates = false; - NL_ATTR_FOR_EACH (a, left, actions, actions_len) { - uint16_t type = nl_attr_type(a); - int len = odp_action_len(type); - - if (len != nl_attr_get_size(a)) { - return EINVAL; - } - - switch (type) { - case OVS_ACTION_ATTR_OUTPUT: - if (nl_attr_get_u32(a) >= MAX_PORTS) { - return EINVAL; - } - break; - - case OVS_ACTION_ATTR_USERSPACE: - break; - - case OVS_ACTION_ATTR_PUSH_VLAN: - *mutates = true; - if (nl_attr_get_be16(a) & htons(VLAN_CFI)) { - return EINVAL; - } - break; - - case OVS_ACTION_ATTR_SET_NW_TOS: - *mutates = true; - if (nl_attr_get_u8(a) & IP_ECN_MASK) { - return EINVAL; - } - break; - - case OVS_ACTION_ATTR_POP_VLAN: - case OVS_ACTION_ATTR_SET_DL_SRC: - case OVS_ACTION_ATTR_SET_DL_DST: - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - *mutates = true; - break; - - case OVS_ACTION_ATTR_SET_TUNNEL: - case OVS_ACTION_ATTR_SET_PRIORITY: - case OVS_ACTION_ATTR_POP_PRIORITY: - default: - return EOPNOTSUPP; - } - } - return 0; -} - static int set_flow_actions(struct dp_netdev_flow *flow, const struct nlattr *actions, size_t actions_len) { - bool mutates; - int error; - - error = dpif_netdev_validate_actions(actions, actions_len, &mutates); - if (error) { - return error; - } - flow->actions = xrealloc(flow->actions, actions_len); flow->actions_len = actions_len; memcpy(flow->actions, actions, actions_len); @@ -956,7 +869,6 @@ dpif_netdev_execute(struct dpif *dpif, { struct dp_netdev *dp = get_dp_netdev(dpif); struct ofpbuf copy; - bool mutates; struct flow key; int error; @@ -964,34 +876,19 @@ dpif_netdev_execute(struct dpif *dpif, return EINVAL; } - error = dpif_netdev_validate_actions(actions, actions_len, &mutates); - if (error) { - return error; - } - - if (mutates) { - /* We need a deep copy of 'packet' since we're going to modify its - * data. */ - ofpbuf_init(©, DP_NETDEV_HEADROOM + packet->size); - ofpbuf_reserve(©, DP_NETDEV_HEADROOM); - ofpbuf_put(©, packet->data, packet->size); - } else { - /* We still need a shallow copy of 'packet', even though we won't - * modify its data, because flow_extract() modifies packet->l2, etc. - * We could probably get away with modifying those but it's more polite - * if we don't. */ - copy = *packet; - } + /* Make a deep copy of 'packet', because we might modify its data. */ + ofpbuf_init(©, DP_NETDEV_HEADROOM + packet->size); + ofpbuf_reserve(©, DP_NETDEV_HEADROOM); + ofpbuf_put(©, packet->data, packet->size); - flow_extract(©, 0, -1, &key); + flow_extract(©, 0, 0, -1, &key); error = dpif_netdev_flow_from_nlattrs(key_attrs, key_len, &key); if (!error) { - error = dp_netdev_execute_actions(dp, ©, &key, - actions, actions_len); - } - if (mutates) { - ofpbuf_uninit(©); + dp_netdev_execute_actions(dp, ©, &key, + actions, actions_len); } + + ofpbuf_uninit(©); return error; } @@ -1084,11 +981,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port, if (packet->size < ETH_HEADER_LEN) { return; } - if (flow_extract(packet, 0, port->port_no, &key) && dp->drop_frags) { - dp->n_frags++; - return; - } - + flow_extract(packet, 0, 0, port->port_no, &key); flow = dp_netdev_lookup_flow(dp, &key); if (flow) { dp_netdev_flow_used(flow, &key, packet); @@ -1159,94 +1052,104 @@ dp_netdev_pop_vlan(struct ofpbuf *packet) } static void -dp_netdev_set_dl_src(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN]) +dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key) { struct eth_header *eh = packet->l2; - memcpy(eh->eth_src, dl_addr, sizeof eh->eth_src); + + memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src); + memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst); } static void -dp_netdev_set_dl_dst(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN]) +dp_netdev_set_ip_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr) { - struct eth_header *eh = packet->l2; - memcpy(eh->eth_dst, dl_addr, sizeof eh->eth_dst); + struct ip_header *nh = packet->l3; + + if (nh->ip_proto == IPPROTO_TCP && packet->l7) { + struct tcp_header *th = packet->l4; + th->tcp_csum = recalc_csum32(th->tcp_csum, *addr, new_addr); + } else if (nh->ip_proto == IPPROTO_UDP && packet->l7) { + struct udp_header *uh = packet->l4; + if (uh->udp_csum) { + uh->udp_csum = recalc_csum32(uh->udp_csum, *addr, new_addr); + if (!uh->udp_csum) { + uh->udp_csum = htons(0xffff); + } + } + } + nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr); + *addr = new_addr; } -static bool -is_ip(const struct ofpbuf *packet, const struct flow *key) +static void +dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos) { - return key->dl_type == htons(ETH_TYPE_IP) && packet->l4; + uint8_t *field = &nh->ip_tos; + + nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field), + htons((uint16_t) new_tos)); + *field = new_tos; } static void -dp_netdev_set_nw_addr(struct ofpbuf *packet, const struct flow *key, - const struct nlattr *a) -{ - if (is_ip(packet, key)) { - struct ip_header *nh = packet->l3; - ovs_be32 ip = nl_attr_get_be32(a); - uint16_t type = nl_attr_type(a); - ovs_be32 *field; - - field = type == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst; - if (key->nw_proto == IPPROTO_TCP && packet->l7) { - struct tcp_header *th = packet->l4; - th->tcp_csum = recalc_csum32(th->tcp_csum, *field, ip); - } else if (key->nw_proto == IPPROTO_UDP && packet->l7) { - struct udp_header *uh = packet->l4; - if (uh->udp_csum) { - uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip); - if (!uh->udp_csum) { - uh->udp_csum = htons(0xffff); - } - } - } - nh->ip_csum = recalc_csum32(nh->ip_csum, *field, ip); - *field = ip; +dp_netdev_set_ip_ttl(struct ip_header *nh, uint8_t new_ttl) +{ + uint8_t *field = &nh->ip_ttl; + + nh->ip_csum = recalc_csum16(nh->ip_csum, htons(*field << 8), + htons(new_ttl << 8)); + *field = new_ttl; +} + +static void +dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key) +{ + struct ip_header *nh = packet->l3; + + if (nh->ip_src != ipv4_key->ipv4_src) { + dp_netdev_set_ip_addr(packet, &nh->ip_src, ipv4_key->ipv4_src); + } + if (nh->ip_dst != ipv4_key->ipv4_dst) { + dp_netdev_set_ip_addr(packet, &nh->ip_dst, ipv4_key->ipv4_dst); + } + if (nh->ip_tos != ipv4_key->ipv4_tos) { + dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos); + } + if (nh->ip_ttl != ipv4_key->ipv4_ttl) { + dp_netdev_set_ip_ttl(nh, ipv4_key->ipv4_ttl); } } static void -dp_netdev_set_nw_tos(struct ofpbuf *packet, const struct flow *key, - uint8_t nw_tos) +dp_netdev_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum) { - if (is_ip(packet, key)) { - struct ip_header *nh = packet->l3; - uint8_t *field = &nh->ip_tos; + *csum = recalc_csum16(*csum, *port, new_port); + *port = new_port; +} - /* Set the DSCP bits and preserve the ECN bits. */ - uint8_t new = nw_tos | (nh->ip_tos & IP_ECN_MASK); +static void +dp_netdev_set_tcp_port(struct ofpbuf *packet, const struct ovs_key_tcp *tcp_key) +{ + struct tcp_header *th = packet->l4; - nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field), - htons((uint16_t) new)); - *field = new; + if (th->tcp_src != tcp_key->tcp_src) { + dp_netdev_set_port(&th->tcp_src, tcp_key->tcp_src, &th->tcp_csum); + } + if (th->tcp_dst != tcp_key->tcp_dst) { + dp_netdev_set_port(&th->tcp_dst, tcp_key->tcp_dst, &th->tcp_csum); } } static void -dp_netdev_set_tp_port(struct ofpbuf *packet, const struct flow *key, - const struct nlattr *a) -{ - if (is_ip(packet, key)) { - uint16_t type = nl_attr_type(a); - ovs_be16 port = nl_attr_get_be16(a); - ovs_be16 *field; - - if (key->nw_proto == IPPROTO_TCP && packet->l7) { - struct tcp_header *th = packet->l4; - field = (type == OVS_ACTION_ATTR_SET_TP_SRC - ? &th->tcp_src : &th->tcp_dst); - th->tcp_csum = recalc_csum16(th->tcp_csum, *field, port); - *field = port; - } else if (key->nw_proto == IPPROTO_UDP && packet->l7) { - struct udp_header *uh = packet->l4; - field = (type == OVS_ACTION_ATTR_SET_TP_SRC - ? &uh->udp_src : &uh->udp_dst); - uh->udp_csum = recalc_csum16(uh->udp_csum, *field, port); - *field = port; - } else { - return; - } +dp_netdev_set_udp_port(struct ofpbuf *packet, const struct ovs_key_udp *udp_key) +{ + struct udp_header *uh = packet->l4; + + if (uh->udp_src != udp_key->udp_src) { + dp_netdev_set_port(&uh->udp_src, udp_key->udp_src, &uh->udp_csum); + } + if (uh->udp_dst != udp_key->udp_dst) { + dp_netdev_set_port(&uh->udp_dst, udp_key->udp_dst, &uh->udp_csum); } } @@ -1293,7 +1196,100 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet, return 0; } -static int +static void +dp_netdev_sample(struct dp_netdev *dp, + struct ofpbuf *packet, struct flow *key, + const struct nlattr *action) +{ + const struct nlattr *subactions = NULL; + const struct nlattr *a; + size_t left; + + NL_NESTED_FOR_EACH_UNSAFE (a, left, action) { + int type = nl_attr_type(a); + + switch ((enum ovs_sample_attr) type) { + case OVS_SAMPLE_ATTR_PROBABILITY: + if (random_uint32() >= nl_attr_get_u32(a)) { + return; + } + break; + + case OVS_SAMPLE_ATTR_ACTIONS: + subactions = a; + break; + + case OVS_SAMPLE_ATTR_UNSPEC: + case __OVS_SAMPLE_ATTR_MAX: + default: + NOT_REACHED(); + } + } + + dp_netdev_execute_actions(dp, packet, key, nl_attr_get(subactions), + nl_attr_get_size(subactions)); +} + +static void +dp_netdev_action_userspace(struct dp_netdev *dp, + struct ofpbuf *packet, struct flow *key, + const struct nlattr *a) +{ + const struct nlattr *userdata_attr; + uint64_t userdata; + + userdata_attr = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA); + userdata = userdata_attr ? nl_attr_get_u64(userdata_attr) : 0; + dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata); +} + +static void +execute_set_action(struct ofpbuf *packet, const struct nlattr *a) +{ + enum ovs_key_attr type = nl_attr_type(a); + switch (type) { + case OVS_KEY_ATTR_TUN_ID: + case OVS_KEY_ATTR_PRIORITY: + /* not implemented */ + break; + + case OVS_KEY_ATTR_ETHERNET: + dp_netdev_set_dl(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet))); + break; + + case OVS_KEY_ATTR_IPV4: + dp_netdev_set_ipv4(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4))); + break; + + case OVS_KEY_ATTR_TCP: + dp_netdev_set_tcp_port(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp))); + break; + + case OVS_KEY_ATTR_UDP: + dp_netdev_set_udp_port(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_udp))); + break; + + case OVS_KEY_ATTR_UNSPEC: + case OVS_KEY_ATTR_ENCAP: + case OVS_KEY_ATTR_ETHERTYPE: + case OVS_KEY_ATTR_IPV6: + case OVS_KEY_ATTR_IN_PORT: + case OVS_KEY_ATTR_VLAN: + case OVS_KEY_ATTR_ICMP: + case OVS_KEY_ATTR_ICMPV6: + case OVS_KEY_ATTR_ARP: + case OVS_KEY_ATTR_ND: + case __OVS_KEY_ATTR_MAX: + default: + NOT_REACHED(); + } +} + +static void dp_netdev_execute_actions(struct dp_netdev *dp, struct ofpbuf *packet, struct flow *key, const struct nlattr *actions, @@ -1303,48 +1299,40 @@ dp_netdev_execute_actions(struct dp_netdev *dp, unsigned int left; NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) { - switch (nl_attr_type(a)) { + const struct ovs_action_push_vlan *vlan; + int type = nl_attr_type(a); + + switch ((enum ovs_action_attr) type) { case OVS_ACTION_ATTR_OUTPUT: dp_netdev_output_port(dp, packet, nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_USERSPACE: - dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, - key, nl_attr_get_u64(a)); + dp_netdev_action_userspace(dp, packet, key, a); break; case OVS_ACTION_ATTR_PUSH_VLAN: - eth_push_vlan(packet, nl_attr_get_be16(a)); + vlan = nl_attr_get(a); + eth_push_vlan(packet, vlan->vlan_tci); break; case OVS_ACTION_ATTR_POP_VLAN: dp_netdev_pop_vlan(packet); break; - case OVS_ACTION_ATTR_SET_DL_SRC: - dp_netdev_set_dl_src(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN)); - break; - - case OVS_ACTION_ATTR_SET_DL_DST: - dp_netdev_set_dl_dst(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN)); + case OVS_ACTION_ATTR_SET: + execute_set_action(packet, nl_attr_get(a)); break; - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - dp_netdev_set_nw_addr(packet, key, a); + case OVS_ACTION_ATTR_SAMPLE: + dp_netdev_sample(dp, packet, key, a); break; - case OVS_ACTION_ATTR_SET_NW_TOS: - dp_netdev_set_nw_tos(packet, key, nl_attr_get_u8(a)); - break; - - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - dp_netdev_set_tp_port(packet, key, a); - break; + case OVS_ACTION_ATTR_UNSPEC: + case __OVS_ACTION_ATTR_MAX: + NOT_REACHED(); } } - return 0; } const struct dpif_class dpif_netdev_class = { @@ -1356,13 +1344,12 @@ const struct dpif_class dpif_netdev_class = { dpif_netdev_run, dpif_netdev_wait, dpif_netdev_get_stats, - dpif_netdev_get_drop_frags, - dpif_netdev_set_drop_frags, dpif_netdev_port_add, dpif_netdev_port_del, dpif_netdev_port_query_by_number, dpif_netdev_port_query_by_name, dpif_netdev_get_max_ports, + NULL, /* port_get_pid */ dpif_netdev_port_dump_start, dpif_netdev_port_dump_next, dpif_netdev_port_dump_done, @@ -1376,6 +1363,7 @@ const struct dpif_class dpif_netdev_class = { dpif_netdev_flow_dump_next, dpif_netdev_flow_dump_done, dpif_netdev_execute, + NULL, /* operate */ dpif_netdev_recv_get_mask, dpif_netdev_recv_set_mask, NULL, /* queue_to_priority */