-static int do_output(struct datapath *dp, struct sk_buff *skb, size_t max_len,
- int out_port)
-{
- if (!skb)
- return -ENOMEM;
- return (likely(out_port != OFPP_CONTROLLER)
- ? dp_output_port(dp, skb, out_port)
- : dp_output_control(dp, skb, fwd_save_skb(skb),
- max_len, OFPR_ACTION));
-}
-
-void execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct sw_flow_key *key,
- const struct ofp_action *actions, int n_actions)
-{
- /* Every output action needs a separate clone of 'skb', but the common
- * case is just a single output action, so that doing a clone and
- * then freeing the original skbuff 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->dl_type);
-
- for (i = 0; i < n_actions; i++) {
- const struct ofp_action *a = &actions[i];
-
- if (prev_port != -1) {
- do_output(dp, skb_clone(skb, GFP_ATOMIC),
- max_len, prev_port);
- prev_port = -1;
- }
-
- if (likely(a->type == htons(OFPAT_OUTPUT))) {
- prev_port = ntohs(a->arg.output.port);
- max_len = ntohs(a->arg.output.max_len);
- } else {
- if (!make_writable(&skb)) {
- 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)
- do_output(dp, skb, max_len, prev_port);
- else
- kfree_skb(skb);
-}
-
-/* Updates 'sum', which is a field in 'skb''s data, given that a 4-byte field
- * covered by the sum has been changed from 'from' to 'to'. If set,
- * 'pseudohdr' indicates that the field is in the TCP or UDP pseudo-header.
- * Based on nf_proto_csum_replace4. */
-static void update_csum(__sum16 *sum, struct sk_buff *skb,
- __be32 from, __be32 to, int pseudohdr)
-{
- __be32 diff[] = { ~from, to };
- if (skb->ip_summed != CHECKSUM_PARTIAL) {
- *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
- ~csum_unfold(*sum)));
- if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
- skb->csum = ~csum_partial((char *)diff, sizeof(diff),
- ~skb->csum);
- } else if (pseudohdr)
- *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
- csum_unfold(*sum)));
-}
-
-static void modify_nh(struct sk_buff *skb, uint16_t eth_proto,
- uint8_t nw_proto, const struct ofp_action *a)
-{
- if (eth_proto == ETH_P_IP) {
- struct iphdr *nh = ip_hdr(skb);
- uint32_t new, *field;
-
- new = a->arg.nw_addr;
-
- if (a->type == htons(OFPAT_SET_NW_SRC))
- field = &nh->saddr;
- else
- field = &nh->daddr;
-
- if (nw_proto == IPPROTO_TCP) {
- struct tcphdr *th = tcp_hdr(skb);
- update_csum(&th->check, skb, *field, new, 1);
- } else if (nw_proto == IPPROTO_UDP) {
- struct udphdr *th = udp_hdr(skb);
- update_csum(&th->check, skb, *field, new, 1);
- }
- update_csum(&nh->check, skb, *field, new, 0);
- *field = new;
- }
-}
-
-static void modify_th(struct sk_buff *skb, uint16_t eth_proto,
- uint8_t nw_proto, const struct ofp_action *a)
-{
- if (eth_proto == ETH_P_IP) {
- uint16_t new, *field;
-
- new = a->arg.tp;
-
- if (nw_proto == IPPROTO_TCP) {
- struct tcphdr *th = tcp_hdr(skb);
-
- if (a->type == htons(OFPAT_SET_TP_SRC))
- field = &th->source;
- else
- field = &th->dest;
-
- update_csum(&th->check, skb, *field, new, 1);
- *field = new;
- } else if (nw_proto == IPPROTO_UDP) {
- struct udphdr *th = udp_hdr(skb);
-
- if (a->type == htons(OFPAT_SET_TP_SRC))
- field = &th->source;
- else
- field = &th->dest;
-
- update_csum(&th->check, skb, *field, new, 1);
- *field = new;
- }
- }
-}
-
-static struct sk_buff *vlan_pull_tag(struct sk_buff *skb)
-{
- struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
- struct ethhdr *eh;
-
-
- /* Verify we were given a vlan packet */
- if (vh->h_vlan_proto != htons(ETH_P_8021Q))
- return skb;
-
- memmove(skb->data + VLAN_HLEN, skb->data, 2 * VLAN_ETH_ALEN);
-
- eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
-
- skb->protocol = eh->h_proto;
- skb->mac_header += VLAN_HLEN;
-
- return skb;
-}
-
-static struct sk_buff *modify_vlan(struct sk_buff *skb,
- const struct sw_flow_key *key, const struct ofp_action *a)