-static void
-do_output(struct datapath *dp, struct buffer *buffer, int in_port,
- size_t max_len, int out_port, bool ignore_no_fwd)
-{
- if (out_port != OFPP_CONTROLLER) {
- dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd);
- } else {
- dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
- }
-}
-
-static void
-execute_actions(struct datapath *dp, struct buffer *buffer,
- int in_port, const struct sw_flow_key *key,
- const struct ofp_action *actions, int n_actions,
- bool ignore_no_fwd)
-{
- /* Every output action needs a separate clone of 'buffer', but the common
- * case is just a single output action, so that doing a clone and then
- * freeing the original buffer is wasteful. So the following code is
- * slightly obscure just to avoid that. */
- int prev_port;
- size_t max_len=0; /* Initialze to make compiler happy */
- uint16_t eth_proto;
- int i;
-
- prev_port = -1;
- eth_proto = ntohs(key->flow.dl_type);
-
- for (i = 0; i < n_actions; i++) {
- const struct ofp_action *a = &actions[i];
- struct eth_header *eh = buffer->l2;
-
- if (prev_port != -1) {
- do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port,
- ignore_no_fwd);
- prev_port = -1;
- }
-
- switch (ntohs(a->type)) {
- case OFPAT_OUTPUT:
- prev_port = ntohs(a->arg.output.port);
- max_len = ntohs(a->arg.output.max_len);
- break;
-
- case OFPAT_SET_DL_VLAN:
- modify_vlan(buffer, key, a);
- break;
-
- case OFPAT_SET_DL_SRC:
- memcpy(eh->eth_src, a->arg.dl_addr, sizeof eh->eth_src);
- break;
-
- case OFPAT_SET_DL_DST:
- memcpy(eh->eth_dst, a->arg.dl_addr, sizeof eh->eth_dst);
- break;
-
- case OFPAT_SET_NW_SRC:
- case OFPAT_SET_NW_DST:
- modify_nh(buffer, eth_proto, key->flow.nw_proto, a);
- break;
-
- case OFPAT_SET_TP_SRC:
- case OFPAT_SET_TP_DST:
- modify_th(buffer, eth_proto, key->flow.nw_proto, a);
- break;
-
- default:
- NOT_REACHED();
- }
- }
- if (prev_port != -1)
- do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd);
- else
- buffer_delete(buffer);
-}
-
-static void modify_nh(struct buffer *buffer, uint16_t eth_proto,
- uint8_t nw_proto, const struct ofp_action *a)
-{
- if (eth_proto == ETH_TYPE_IP) {
- struct ip_header *nh = buffer->l3;
- uint32_t new, *field;
-
- new = a->arg.nw_addr;
- field = a->type == OFPAT_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
- if (nw_proto == IP_TYPE_TCP) {
- struct tcp_header *th = buffer->l4;
- th->tcp_csum = recalc_csum32(th->tcp_csum, *field, new);
- } else if (nw_proto == IP_TYPE_UDP) {
- struct udp_header *th = buffer->l4;
- if (th->udp_csum) {
- th->udp_csum = recalc_csum32(th->udp_csum, *field, new);
- if (!th->udp_csum) {
- th->udp_csum = 0xffff;
- }
- }
- }
- nh->ip_csum = recalc_csum32(nh->ip_csum, *field, new);
- *field = new;
- }
-}
-
-static void modify_th(struct buffer *buffer, uint16_t eth_proto,
- uint8_t nw_proto, const struct ofp_action *a)
-{
- if (eth_proto == ETH_TYPE_IP) {
- uint16_t new, *field;
-
- new = a->arg.tp;
-
- if (nw_proto == IP_TYPE_TCP) {
- struct tcp_header *th = buffer->l4;
- field = a->type == OFPAT_SET_TP_SRC ? &th->tcp_src : &th->tcp_dst;
- th->tcp_csum = recalc_csum16(th->tcp_csum, *field, new);
- *field = new;
- } else if (nw_proto == IP_TYPE_UDP) {
- struct udp_header *th = buffer->l4;
- field = a->type == OFPAT_SET_TP_SRC ? &th->udp_src : &th->udp_dst;
- th->udp_csum = recalc_csum16(th->udp_csum, *field, new);
- *field = new;
- }
- }
-}
-
-static void
-modify_vlan(struct buffer *buffer,
- const struct sw_flow_key *key, const struct ofp_action *a)
-{
- uint16_t new_id = a->arg.vlan_id;
- struct vlan_eth_header *veh;
-
- if (new_id != htons(OFP_VLAN_NONE)) {
- if (key->flow.dl_vlan != htons(OFP_VLAN_NONE)) {
- /* Modify vlan id, but maintain other TCI values */
- veh = buffer->l2;
- veh->veth_tci &= ~htons(VLAN_VID);
- veh->veth_tci |= new_id;
- } else {
- /* Insert new vlan id. */
- struct eth_header *eh = buffer->l2;
- struct vlan_eth_header tmp;
- memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
- memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
- tmp.veth_type = htons(ETH_TYPE_VLAN);
- tmp.veth_tci = new_id;
- tmp.veth_next_type = eh->eth_type;
-
- veh = buffer_push_uninit(buffer, VLAN_HEADER_LEN);
- memcpy(veh, &tmp, sizeof tmp);
- buffer->l2 -= VLAN_HEADER_LEN;
- }
- } else {
- /* Remove an existing vlan header if it exists */
- veh = buffer->l2;
- if (veh->veth_type == htons(ETH_TYPE_VLAN)) {
- struct eth_header tmp;
-
- memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
- memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
- tmp.eth_type = veh->veth_next_type;
-
- buffer->size -= VLAN_HEADER_LEN;
- buffer->data += VLAN_HEADER_LEN;
- buffer->l2 += VLAN_HEADER_LEN;
- memcpy(buffer->data, &tmp, sizeof tmp);
- }
- }
-}
-