X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fvport-gre.c;h=2bddc871093205c6442a24e4954c5bd538b47693;hb=c71270b7aefddd967d7dd5446f7701241380b09d;hp=40dd330ee257e778240f7873c01e5c3332c167f3;hpb=2736b84e2348090d4756f987db37aa7ddfcb50e0;p=openvswitch diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c index 40dd330e..2bddc871 100644 --- a/datapath/vport-gre.c +++ b/datapath/vport-gre.c @@ -22,7 +22,9 @@ #include #include #include +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #include +#endif #include #include #include @@ -32,6 +34,7 @@ #include "openvswitch/gre.h" #include "table.h" #include "vport.h" +#include "vport-generic.h" /* The absolute minimum fragment size. Note that there are many other * definitions of the minimum MTU. */ @@ -41,6 +44,11 @@ * number of options. */ #define GRE_HEADER_SECTION 4 +struct gre_base_hdr { + __be16 flags; + __be16 protocol; +}; + struct mutable_config { struct rcu_head rcu; @@ -52,6 +60,7 @@ struct mutable_config { }; struct gre_vport { + struct rcu_head rcu; struct tbl_node tbl_node; char name[IFNAMSIZ]; @@ -60,8 +69,6 @@ struct gre_vport { struct mutable_config *mutable; }; -struct vport_ops gre_vport_ops; - /* Protected by RCU. */ static struct tbl *port_table; @@ -73,34 +80,30 @@ static unsigned int key_remote_ports; static unsigned int local_remote_ports; static unsigned int remote_ports; -static inline struct gre_vport * -gre_vport_priv(const struct vport *vport) +static inline struct gre_vport *gre_vport_priv(const struct vport *vport) { return vport_priv(vport); } -static inline struct vport * -gre_vport_to_vport(const struct gre_vport *gre_vport) +static inline struct vport *gre_vport_to_vport(const struct gre_vport *gre_vport) { return vport_from_priv(gre_vport); } -static inline struct gre_vport * -gre_vport_table_cast(const struct tbl_node *node) +static inline struct gre_vport *gre_vport_table_cast(const struct tbl_node *node) { return container_of(node, struct gre_vport, tbl_node); } /* RCU callback. */ -static void -free_config(struct rcu_head *rcu) +static void free_config(struct rcu_head *rcu) { struct mutable_config *c = container_of(rcu, struct mutable_config, rcu); kfree(c); } -static void -assign_config_rcu(struct vport *vport, struct mutable_config *new_config) +static void assign_config_rcu(struct vport *vport, + struct mutable_config *new_config) { struct gre_vport *gre_vport = gre_vport_priv(vport); struct mutable_config *old_config; @@ -110,8 +113,7 @@ assign_config_rcu(struct vport *vport, struct mutable_config *new_config) call_rcu(&old_config->rcu, free_config); } -static unsigned int * -find_port_pool(const struct mutable_config *mutable) +static unsigned int *find_port_pool(const struct mutable_config *mutable) { if (mutable->port_config.flags & GRE_F_IN_KEY_MATCH) { if (mutable->port_config.saddr) @@ -140,8 +142,7 @@ struct port_lookup_key { /* Modifies 'target' to store the rcu_dereferenced pointer that was used to do * the comparision. */ -static int -port_cmp(const struct tbl_node *node, void *target) +static int port_cmp(const struct tbl_node *node, void *target) { const struct gre_vport *gre_vport = gre_vport_table_cast(node); struct port_lookup_key *lookup = target; @@ -155,14 +156,12 @@ port_cmp(const struct tbl_node *node, void *target) lookup->mutable->port_config.saddr == lookup->vals[LOOKUP_SADDR]; } -static u32 -port_hash(struct port_lookup_key *lookup) +static u32 port_hash(struct port_lookup_key *lookup) { return jhash2(lookup->vals, ARRAY_SIZE(lookup->vals), 0); } -static int -add_port(struct vport *vport) +static int add_port(struct vport *vport) { struct gre_vport *gre_vport = gre_vport_priv(vport); struct port_lookup_key lookup; @@ -203,8 +202,7 @@ add_port(struct vport *vport) return 0; } -static int -del_port(struct vport *vport) +static int del_port(struct vport *vport) { struct gre_vport *gre_vport = gre_vport_priv(vport); int err; @@ -222,9 +220,9 @@ del_port(struct vport *vport) #define FIND_PORT_MATCH (1 << 1) #define FIND_PORT_ANY (FIND_PORT_KEY | FIND_PORT_MATCH) -static struct vport * -find_port(__be32 saddr, __be32 daddr, __be32 key, int port_type, - const struct mutable_config **mutable) +static struct vport *find_port(__be32 saddr, __be32 daddr, __be32 key, + int port_type, + const struct mutable_config **mutable) { struct port_lookup_key lookup; struct tbl *table = rcu_dereference(port_table); @@ -283,8 +281,7 @@ found: return gre_vport_to_vport(gre_vport_table_cast(tbl_node)); } -static bool -check_ipv4_address(__be32 addr) +static bool check_ipv4_address(__be32 addr) { if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) || ipv4_is_loopback(addr) || ipv4_is_zeronet(addr)) @@ -293,8 +290,7 @@ check_ipv4_address(__be32 addr) return true; } -static bool -ipv4_should_icmp(struct sk_buff *skb) +static bool ipv4_should_icmp(struct sk_buff *skb) { struct iphdr *old_iph = ip_hdr(skb); @@ -334,9 +330,8 @@ ipv4_should_icmp(struct sk_buff *skb) return true; } -static void -ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, - unsigned int mtu, unsigned int payload_length) +static void ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, + unsigned int mtu, unsigned int payload_length) { struct iphdr *iph, *old_iph = ip_hdr(skb); struct icmphdr *icmph; @@ -376,8 +371,8 @@ ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, icmph->checksum = csum_fold(nskb->csum); } -static bool -ipv6_should_icmp(struct sk_buff *skb) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static bool ipv6_should_icmp(struct sk_buff *skb) { struct ipv6hdr *old_ipv6h = ipv6_hdr(skb); int addr_type; @@ -413,9 +408,8 @@ ipv6_should_icmp(struct sk_buff *skb) return true; } -static void -ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, unsigned int mtu, - unsigned int payload_length) +static void ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, + unsigned int mtu, unsigned int payload_length) { struct ipv6hdr *ipv6h, *old_ipv6h = ipv6_hdr(skb); struct icmp6hdr *icmp6h; @@ -451,13 +445,15 @@ ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, unsigned int mtu, + payload_length, ipv6h->nexthdr, nskb->csum); } +#endif /* IPv6 */ -static bool -send_frag_needed(struct vport *vport, const struct mutable_config *mutable, - struct sk_buff *skb, unsigned int mtu) +static bool send_frag_needed(struct vport *vport, + const struct mutable_config *mutable, + struct sk_buff *skb, unsigned int mtu, + __be32 flow_key) { unsigned int eth_hdr_len = ETH_HLEN; - unsigned int total_length, header_length, payload_length; + unsigned int total_length = 0, header_length = 0, payload_length; struct ethhdr *eh, *old_eh = eth_hdr(skb); struct sk_buff *nskb; @@ -468,7 +464,9 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable, if (!ipv4_should_icmp(skb)) return true; - } else { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) { if (mtu < IPV6_MIN_MTU) return false; @@ -480,6 +478,9 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable, if (!ipv6_should_icmp(skb)) return true; } +#endif + else + return false; /* Allocate */ if (old_eh->h_proto == htons(ETH_P_8021Q)) @@ -490,12 +491,16 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable, header_length = sizeof(struct iphdr) + sizeof(struct icmphdr); total_length = min_t(unsigned int, header_length + payload_length, 576); - } else { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else { header_length = sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr); total_length = min_t(unsigned int, header_length + payload_length, IPV6_MIN_MTU); } +#endif + total_length = min(total_length, mutable->mtu); payload_length = total_length - header_length; @@ -522,32 +527,30 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable, /* Protocol */ if (skb->protocol == htons(ETH_P_IP)) ipv4_build_icmp(skb, nskb, mtu, payload_length); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else ipv6_build_icmp(skb, nskb, mtu, payload_length); +#endif /* Assume that flow based keys are symmetric with respect to input * and output and use the key that we were going to put on the * outgoing packet for the fake received packet. If the keys are * not symmetric then PMTUD needs to be disabled since we won't have * any way of synthesizing packets. */ - if (mutable->port_config.flags & GRE_F_IN_KEY_MATCH) { - if (mutable->port_config.flags & GRE_F_OUT_KEY_ACTION) - OVS_CB(nskb)->tun_id = OVS_CB(skb)->tun_id; - else - OVS_CB(nskb)->tun_id = mutable->port_config.out_key; - } + if (mutable->port_config.flags & GRE_F_IN_KEY_MATCH && + mutable->port_config.flags & GRE_F_OUT_KEY_ACTION) + OVS_CB(nskb)->tun_id = flow_key; + compute_ip_summed(nskb, false); vport_receive(vport, nskb); return true; } -static struct sk_buff * -check_headroom(struct sk_buff *skb, int headroom) +static struct sk_buff *check_headroom(struct sk_buff *skb, int headroom) { - if (skb_headroom(skb) < headroom || - (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { - struct sk_buff *nskb = skb_realloc_headroom(skb, headroom); + if (skb_headroom(skb) < headroom || skb_header_cloned(skb)) { + struct sk_buff *nskb = skb_realloc_headroom(skb, headroom + 16); if (!nskb) { kfree_skb(skb); return ERR_PTR(-ENOMEM); @@ -565,22 +568,21 @@ check_headroom(struct sk_buff *skb, int headroom) return skb; } -static void -create_gre_header(struct sk_buff *skb, const struct mutable_config *mutable) +static void create_gre_header(struct sk_buff *skb, + const struct mutable_config *mutable) { struct iphdr *iph = ip_hdr(skb); - __be16 *flags = (__be16 *)(iph + 1); - __be16 *protocol = flags + 1; + struct gre_base_hdr *greh = (struct gre_base_hdr *)(iph + 1); __be32 *options = (__be32 *)((u8 *)iph + mutable->tunnel_hlen - GRE_HEADER_SECTION); - *protocol = htons(ETH_P_TEB); - *flags = 0; + greh->protocol = htons(ETH_P_TEB); + greh->flags = 0; /* Work backwards over the options so the checksum is last. */ if (mutable->port_config.out_key || mutable->port_config.flags & GRE_F_OUT_KEY_ACTION) { - *flags |= GRE_KEY; + greh->flags |= GRE_KEY; if (mutable->port_config.flags & GRE_F_OUT_KEY_ACTION) *options = OVS_CB(skb)->tun_id; @@ -591,7 +593,7 @@ create_gre_header(struct sk_buff *skb, const struct mutable_config *mutable) } if (mutable->port_config.flags & GRE_F_OUT_CSUM) { - *flags |= GRE_CSUM; + greh->flags |= GRE_CSUM; *options = 0; *(__sum16 *)options = csum_fold(skb_checksum(skb, @@ -601,8 +603,7 @@ create_gre_header(struct sk_buff *skb, const struct mutable_config *mutable) } } -static int -check_checksum(struct sk_buff *skb) +static int check_checksum(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); __be16 flags = *(__be16 *)(iph + 1); @@ -628,30 +629,29 @@ check_checksum(struct sk_buff *skb) return (csum == 0); } -static int -parse_gre_header(struct iphdr *iph, __be16 *flags, __be32 *key) +static int parse_gre_header(struct iphdr *iph, __be16 *flags, __be32 *key) { - __be16 *flagsp = (__be16 *)(iph + 1); - __be16 *protocol = flagsp + 1; - __be32 *options = (__be32 *)(protocol + 1); + /* IP and ICMP protocol handlers check that the IHL is valid. */ + struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2)); + __be32 *options = (__be32 *)(greh + 1); int hdr_len; - *flags = *flagsp; + *flags = greh->flags; - if (*flags & (GRE_VERSION | GRE_ROUTING)) + if (greh->flags & (GRE_VERSION | GRE_ROUTING)) return -EINVAL; - if (*protocol != htons(ETH_P_TEB)) + if (greh->protocol != htons(ETH_P_TEB)) return -EINVAL; hdr_len = GRE_HEADER_SECTION; - if (*flags & GRE_CSUM) { + if (greh->flags & GRE_CSUM) { hdr_len += GRE_HEADER_SECTION; options++; } - if (*flags & GRE_KEY) { + if (greh->flags & GRE_KEY) { hdr_len += GRE_HEADER_SECTION; *key = *options; @@ -659,29 +659,29 @@ parse_gre_header(struct iphdr *iph, __be16 *flags, __be32 *key) } else *key = 0; - if (*flags & GRE_SEQ) + if (greh->flags & GRE_SEQ) hdr_len += GRE_HEADER_SECTION; return hdr_len; } -static inline u8 -ecn_encapsulate(u8 tos, struct sk_buff *skb) +static inline u8 ecn_encapsulate(u8 tos, struct sk_buff *skb) { u8 inner; if (skb->protocol == htons(ETH_P_IP)) inner = ((struct iphdr *)skb_network_header(skb))->tos; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) inner = ipv6_get_dsfield((struct ipv6hdr *)skb_network_header(skb)); +#endif else inner = 0; return INET_ECN_encapsulate(tos, inner); } -static inline void -ecn_decapsulate(u8 tos, struct sk_buff *skb) +static inline void ecn_decapsulate(u8 tos, struct sk_buff *skb) { if (INET_ECN_is_ce(tos)) { __be16 protocol = skb->protocol; @@ -701,7 +701,9 @@ ecn_decapsulate(u8 tos, struct sk_buff *skb) return; IP_ECN_set_ce((struct iphdr *)(nw_header + skb->data)); - } else if (protocol == htons(ETH_P_IPV6)) { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (protocol == htons(ETH_P_IPV6)) { if (unlikely(!pskb_may_pull(skb, nw_header + sizeof(struct ipv6hdr)))) return; @@ -709,14 +711,14 @@ ecn_decapsulate(u8 tos, struct sk_buff *skb) IP6_ECN_set_ce((struct ipv6hdr *)(nw_header + skb->data)); } +#endif } } -static struct sk_buff * -handle_gso(struct sk_buff *skb) +static struct sk_buff *handle_gso(struct sk_buff *skb) { if (skb_is_gso(skb)) { - struct sk_buff *nskb = skb_gso_segment(skb, NETIF_F_SG); + struct sk_buff *nskb = skb_gso_segment(skb, 0); dev_kfree_skb(skb); return nskb; @@ -725,18 +727,18 @@ handle_gso(struct sk_buff *skb) return skb; } -static int -handle_csum_offload(struct sk_buff *skb) +static int handle_csum_offload(struct sk_buff *skb) { if (skb->ip_summed == CHECKSUM_PARTIAL) return skb_checksum_help(skb); - else + else { + skb->ip_summed = CHECKSUM_NONE; return 0; + } } -/* Called with rcu_read_lock and bottom-halves disabled. */ -static void -gre_err(struct sk_buff *skb, u32 info) +/* Called with rcu_read_lock. */ +static void gre_err(struct sk_buff *skb, u32 info) { struct vport *vport; const struct mutable_config *mutable; @@ -771,16 +773,30 @@ gre_err(struct sk_buff *skb, u32 info) if (!vport) return; - if ((mutable->port_config.flags & GRE_F_IN_CSUM) && !(flags & GRE_CSUM)) + /* Packets received by this function were previously sent by us, so + * any comparisons should be to the output values, not the input. + * However, it's not really worth it to have a hash table based on + * output keys (especially since ICMP error handling of tunneled packets + * isn't that reliable anyways). Therefore, we do a lookup based on the + * out key as if it were the in key and then check to see if the input + * and output keys are the same. */ + if (mutable->port_config.in_key != mutable->port_config.out_key) return; - tot_hdr_len = sizeof(struct iphdr) + tunnel_hdr_len; + if (!!(mutable->port_config.flags & GRE_F_IN_KEY_MATCH) != + !!(mutable->port_config.flags & GRE_F_OUT_KEY_ACTION)) + return; + + if ((mutable->port_config.flags & GRE_F_OUT_CSUM) && !(flags & GRE_CSUM)) + return; + + tunnel_hdr_len += iph->ihl << 2; orig_mac_header = skb_mac_header(skb) - skb->data; orig_nw_header = skb_network_header(skb) - skb->data; - skb_set_mac_header(skb, tot_hdr_len); + skb_set_mac_header(skb, tunnel_hdr_len); - tot_hdr_len += ETH_HLEN; + tot_hdr_len = tunnel_hdr_len + ETH_HLEN; skb->protocol = eth_hdr(skb)->h_proto; if (skb->protocol == htons(ETH_P_8021Q)) { @@ -788,19 +804,21 @@ gre_err(struct sk_buff *skb, u32 info) skb->protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; } + skb_set_network_header(skb, tot_hdr_len); + mtu -= tot_hdr_len; + if (skb->protocol == htons(ETH_P_IP)) tot_hdr_len += sizeof(struct iphdr); - else if (skb->protocol == htons(ETH_P_IP)) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) tot_hdr_len += sizeof(struct ipv6hdr); +#endif else goto out; if (!pskb_may_pull(skb, tot_hdr_len)) goto out; - skb_set_network_header(skb, tot_hdr_len); - mtu -= tot_hdr_len; - if (skb->protocol == htons(ETH_P_IP)) { if (mtu < IP_MIN_MTU) { if (ntohs(ip_hdr(skb)->tot_len) >= IP_MIN_MTU) @@ -809,7 +827,9 @@ gre_err(struct sk_buff *skb, u32 info) goto out; } - } else if (skb->protocol == htons(ETH_P_IPV6)) { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) { if (mtu < IPV6_MIN_MTU) { unsigned int packet_length = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); @@ -821,9 +841,10 @@ gre_err(struct sk_buff *skb, u32 info) goto out; } } +#endif __pskb_pull(skb, tunnel_hdr_len); - send_frag_needed(vport, mutable, skb, mtu); + send_frag_needed(vport, mutable, skb, mtu, key); skb_push(skb, tunnel_hdr_len); out: @@ -832,9 +853,8 @@ out: skb->protocol = htons(ETH_P_IP); } -/* Called with rcu_read_lock and bottom-halves disabled. */ -static int -gre_rcv(struct sk_buff *skb) +/* Called with rcu_read_lock. */ +static int gre_rcv(struct sk_buff *skb) { struct vport *vport; const struct mutable_config *mutable; @@ -888,6 +908,8 @@ gre_rcv(struct sk_buff *skb) OVS_CB(skb)->tun_id = 0; skb_push(skb, ETH_HLEN); + compute_ip_summed(skb, false); + vport_receive(vport, skb); return 0; @@ -897,10 +919,9 @@ error: return 0; } -static int -build_packet(struct vport *vport, const struct mutable_config *mutable, - struct iphdr *iph, struct rtable *rt, int max_headroom, int mtu, - struct sk_buff *skb) +static int build_packet(struct vport *vport, const struct mutable_config *mutable, + struct iphdr *iph, struct rtable *rt, int max_headroom, + int mtu, struct sk_buff *skb) { int err; struct iphdr *new_iph; @@ -920,11 +941,13 @@ build_packet(struct vport *vport, const struct mutable_config *mutable, if ((old_iph->frag_off & htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) { - if (send_frag_needed(vport, mutable, skb, mtu)) + if (send_frag_needed(vport, mutable, skb, mtu, OVS_CB(skb)->tun_id)) goto error_free; } - } else if (skb->protocol == htons(ETH_P_IPV6)) { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) { unsigned int packet_length = skb->len - ETH_HLEN - (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q) ? VLAN_HLEN : 0); @@ -933,10 +956,11 @@ build_packet(struct vport *vport, const struct mutable_config *mutable, frag_off = htons(IP_DF); if (mtu < packet_length) { - if (send_frag_needed(vport, mutable, skb, mtu)) + if (send_frag_needed(vport, mutable, skb, mtu, OVS_CB(skb)->tun_id)) goto error_free; } } +#endif skb_reset_transport_header(skb); new_iph = (struct iphdr *)skb_push(skb, mutable->tunnel_hlen); @@ -948,6 +972,10 @@ build_packet(struct vport *vport, const struct mutable_config *mutable, create_gre_header(skb, mutable); + /* Allow our local IP stack to fragment the outer packet even if the + * DF bit is set as a last resort. */ + skb->local_df = 1; + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags = 0; @@ -967,14 +995,12 @@ error: return 0; } -static int -gre_send(struct vport *vport, struct sk_buff *skb) +static int gre_send(struct vport *vport, struct sk_buff *skb) { struct gre_vport *gre_vport = gre_vport_priv(vport); const struct mutable_config *mutable = rcu_dereference(gre_vport->mutable); struct iphdr *old_iph; - struct ipv6hdr *old_ipv6h; int orig_len; struct iphdr iph; struct rtable *rt; @@ -994,21 +1020,24 @@ gre_send(struct vport *vport, struct sk_buff *skb) if (unlikely(!pskb_may_pull(skb, skb_network_header(skb) + sizeof(struct iphdr) - skb->data))) skb->protocol = 0; - } else if (skb->protocol == htons(ETH_P_IPV6)) { + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) { if (unlikely(!pskb_may_pull(skb, skb_network_header(skb) + sizeof(struct ipv6hdr) - skb->data))) skb->protocol = 0; } - +#endif old_iph = ip_hdr(skb); - old_ipv6h = ipv6_hdr(skb); iph.tos = mutable->port_config.tos; if (mutable->port_config.flags & GRE_F_TOS_INHERIT) { if (skb->protocol == htons(ETH_P_IP)) iph.tos = old_iph->tos; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) iph.tos = ipv6_get_dsfield(ipv6_hdr(skb)); +#endif } iph.tos = ecn_encapsulate(iph.tos, skb); @@ -1027,8 +1056,10 @@ gre_send(struct vport *vport, struct sk_buff *skb) if (mutable->port_config.flags & GRE_F_TTL_INHERIT) { if (skb->protocol == htons(ETH_P_IP)) iph.ttl = old_iph->ttl; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) - iph.ttl = old_ipv6h->hop_limit; + iph.ttl = ipv6_hdr(skb)->hop_limit; +#endif } if (!iph.ttl) iph.ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); @@ -1045,9 +1076,11 @@ gre_send(struct vport *vport, struct sk_buff *skb) if (skb->protocol == htons(ETH_P_IP)) { iph.frag_off |= old_iph->frag_off & htons(IP_DF); mtu = max(mtu, IP_MIN_MTU); - - } else if (skb->protocol == htons(ETH_P_IPV6)) + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (skb->protocol == htons(ETH_P_IPV6)) mtu = max(mtu, IPV6_MIN_MTU); +#endif iph.version = 4; iph.ihl = sizeof(struct iphdr) >> 2; @@ -1055,15 +1088,10 @@ gre_send(struct vport *vport, struct sk_buff *skb) iph.daddr = rt->rt_dst; iph.saddr = rt->rt_src; - /* Allow our local IP stack to fragment the outer packet even if the - * DF bit is set as a last resort. */ - skb->local_df = 1; - nf_reset(skb); secpath_reset(skb); skb_dst_drop(skb); skb_dst_set(skb, &rt->u.dst); - skb->ip_summed = CHECKSUM_NONE; /* If we are doing GSO on a pskb it is better to make sure that the * headroom is correct now. We will only have to copy the portion in @@ -1071,7 +1099,9 @@ gre_send(struct vport *vport, struct sk_buff *skb) * the segments. This is particularly beneficial on Xen where we get * lots of GSO pskbs. Conversely, we delay copying if it is just to * get our own writable clone because GSO may do the copy for us. */ - max_headroom = LL_RESERVED_SPACE(rt->u.dst.dev) + mutable->tunnel_hlen; + max_headroom = LL_RESERVED_SPACE(rt->u.dst.dev) + rt->u.dst.header_len + + mutable->tunnel_hlen; + if (skb_headroom(skb) < max_headroom) { skb = check_headroom(skb, max_headroom); if (unlikely(IS_ERR(skb))) { @@ -1080,14 +1110,18 @@ gre_send(struct vport *vport, struct sk_buff *skb) } } - vswitch_skb_checksum_setup(skb); + forward_ip_summed(skb); + + if (unlikely(vswitch_skb_checksum_setup(skb))) + goto error_free; + skb = handle_gso(skb); if (unlikely(IS_ERR(skb))) { vport_record_error(vport, VPORT_E_TX_DROPPED); goto error; } - /* Process GSO segments. Try to do any work on the entire packet that + /* Process GSO segments. Try to do any work for the entire packet that * doesn't involve actually writing to it before this point. */ orig_len = 0; do { @@ -1113,8 +1147,7 @@ static struct net_protocol gre_protocol_handlers = { .err_handler = gre_err, }; -static int -gre_init(void) +static int gre_init(void) { int err; @@ -1125,16 +1158,14 @@ gre_init(void) return err; } -static void -gre_exit(void) +static void gre_exit(void) { tbl_destroy(port_table, NULL); inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE); } -static int -set_config(const struct vport *cur_vport, struct mutable_config *mutable, - const void __user *uconfig) +static int set_config(const struct vport *cur_vport, + struct mutable_config *mutable, const void __user *uconfig) { const struct vport *old_vport; const struct mutable_config *old_mutable; @@ -1160,6 +1191,9 @@ set_config(const struct vport *cur_vport, struct mutable_config *mutable, if (old_vport && old_vport != cur_vport) return -EEXIST; + if (mutable->port_config.flags & GRE_F_OUT_KEY_ACTION) + mutable->port_config.out_key = 0; + mutable->tunnel_hlen = sizeof(struct iphdr) + GRE_HEADER_SECTION; if (mutable->port_config.flags & GRE_F_OUT_CSUM) @@ -1172,8 +1206,7 @@ set_config(const struct vport *cur_vport, struct mutable_config *mutable, return 0; } -static struct vport * -gre_create(const char *name, const void __user *config) +static struct vport *gre_create(const char *name, const void __user *config) { struct vport *vport; struct gre_vport *gre_vport; @@ -1195,7 +1228,7 @@ gre_create(const char *name, const void __user *config) goto error_free_vport; } - vport_gen_ether_addr(gre_vport->mutable->eth_addr); + vport_gen_rand_ether_addr(gre_vport->mutable->eth_addr); gre_vport->mutable->mtu = ETH_DATA_LEN; err = set_config(NULL, gre_vport->mutable, config); @@ -1216,8 +1249,7 @@ error: return ERR_PTR(err); } -static int -gre_modify(struct vport *vport, const void __user *config) +static int gre_modify(struct vport *vport, const void __user *config) { struct gre_vport *gre_vport = gre_vport_priv(vport); struct mutable_config *mutable; @@ -1268,8 +1300,15 @@ error: return err; } -static int -gre_destroy(struct vport *vport) +static void free_port(struct rcu_head *rcu) +{ + struct gre_vport *gre_vport = container_of(rcu, struct gre_vport, rcu); + + kfree(gre_vport->mutable); + vport_free(gre_vport_to_vport(gre_vport)); +} + +static int gre_destroy(struct vport *vport) { struct gre_vport *gre_vport = gre_vport_priv(vport); int port_type; @@ -1287,18 +1326,15 @@ gre_destroy(struct vport *vport) gre_vport->mutable->port_config.in_key, port_type, &old_mutable)) del_port(vport); - kfree(gre_vport->mutable); - vport_free(vport); + call_rcu(&gre_vport->rcu, free_port); return 0; } -static int -gre_set_mtu(struct vport *vport, int mtu) +static int gre_set_mtu(struct vport *vport, int mtu) { struct gre_vport *gre_vport = gre_vport_priv(vport); struct mutable_config *mutable; - struct dp_port *dp_port; mutable = kmemdup(gre_vport->mutable, sizeof(struct mutable_config), GFP_KERNEL); if (!mutable) @@ -1307,15 +1343,10 @@ gre_set_mtu(struct vport *vport, int mtu) mutable->mtu = mtu; assign_config_rcu(vport, mutable); - dp_port = vport_get_dp_port(vport); - if (dp_port) - set_internal_devs_mtu(dp_port->dp); - return 0; } -static int -gre_set_addr(struct vport *vport, const unsigned char *addr) +static int gre_set_addr(struct vport *vport, const unsigned char *addr) { struct gre_vport *gre_vport = gre_vport_priv(vport); struct mutable_config *mutable; @@ -1331,40 +1362,19 @@ gre_set_addr(struct vport *vport, const unsigned char *addr) } -static const char * -gre_get_name(const struct vport *vport) +static const char *gre_get_name(const struct vport *vport) { const struct gre_vport *gre_vport = gre_vport_priv(vport); return gre_vport->name; } -static const unsigned char * -gre_get_addr(const struct vport *vport) +static const unsigned char *gre_get_addr(const struct vport *vport) { const struct gre_vport *gre_vport = gre_vport_priv(vport); return rcu_dereference(gre_vport->mutable)->eth_addr; } -static unsigned -gre_get_dev_flags(const struct vport *vport) -{ - return IFF_UP | IFF_RUNNING | IFF_LOWER_UP; -} - -static int -gre_is_running(const struct vport *vport) -{ - return 1; -} - -static unsigned char -gre_get_operstate(const struct vport *vport) -{ - return IF_OPER_UP; -} - -static int -gre_get_mtu(const struct vport *vport) +static int gre_get_mtu(const struct vport *vport) { const struct gre_vport *gre_vport = gre_vport_priv(vport); return rcu_dereference(gre_vport->mutable)->mtu; @@ -1382,9 +1392,9 @@ struct vport_ops gre_vport_ops = { .set_addr = gre_set_addr, .get_name = gre_get_name, .get_addr = gre_get_addr, - .get_dev_flags = gre_get_dev_flags, - .is_running = gre_is_running, - .get_operstate = gre_get_operstate, + .get_dev_flags = vport_gen_get_dev_flags, + .is_running = vport_gen_is_running, + .get_operstate = vport_gen_get_operstate, .get_mtu = gre_get_mtu, .send = gre_send, };