-static struct vport *tnl_vport_to_vport(const struct tnl_vport *tnl_vport)
-{
- return vport_from_priv(tnl_vport);
-}
-
-static void free_config_rcu(struct rcu_head *rcu)
-{
- struct tnl_mutable_config *c = container_of(rcu, struct tnl_mutable_config, rcu);
- kfree(c);
-}
-
-/* Frees the portion of 'mutable' that requires RTNL and thus can't happen
- * within an RCU callback. Fortunately this part doesn't require waiting for
- * an RCU grace period.
- */
-static void free_mutable_rtnl(struct tnl_mutable_config *mutable)
-{
- ASSERT_RTNL();
- if (ipv4_is_multicast(mutable->key.daddr) && mutable->mlink) {
- struct in_device *in_dev;
- in_dev = inetdev_by_index(port_key_get_net(&mutable->key), mutable->mlink);
- if (in_dev)
- ip_mc_dec_group(in_dev, mutable->key.daddr);
- }
-}
-
-static void assign_config_rcu(struct vport *vport,
- struct tnl_mutable_config *new_config)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *old_config;
-
- old_config = rtnl_dereference(tnl_vport->mutable);
- rcu_assign_pointer(tnl_vport->mutable, new_config);
-
- free_mutable_rtnl(old_config);
- call_rcu(&old_config->rcu, free_config_rcu);
-}
-
-static unsigned int *find_port_pool(const struct tnl_mutable_config *mutable)
-{
- bool is_multicast = ipv4_is_multicast(mutable->key.daddr);
-
- if (mutable->flags & TNL_F_IN_KEY_MATCH) {
- if (mutable->key.saddr)
- return &local_remote_ports;
- else if (is_multicast)
- return &multicast_ports;
- else
- return &remote_ports;
- } else {
- if (mutable->key.saddr)
- return &key_local_remote_ports;
- else if (is_multicast)
- return &key_multicast_ports;
- else if (mutable->key.daddr)
- return &key_remote_ports;
- else
- return &null_ports;
- }
-}
-
-static u32 port_hash(const struct port_lookup_key *key)
-{
- return jhash2((u32 *)key, (PORT_KEY_LEN / sizeof(u32)), 0);
-}
-
-static struct hlist_head *find_bucket(u32 hash)
-{
- return &port_table[(hash & (PORT_TABLE_SIZE - 1))];
-}
-
-static void port_table_add_port(struct vport *vport)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- const struct tnl_mutable_config *mutable;
- u32 hash;
-
- mutable = rtnl_dereference(tnl_vport->mutable);
- hash = port_hash(&mutable->key);
- hlist_add_head_rcu(&tnl_vport->hash_node, find_bucket(hash));
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))++;
-}
-
-static void port_table_move_port(struct vport *vport,
- struct tnl_mutable_config *new_mutable)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- u32 hash;
-
- hash = port_hash(&new_mutable->key);
- hlist_del_init_rcu(&tnl_vport->hash_node);
- hlist_add_head_rcu(&tnl_vport->hash_node, find_bucket(hash));
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))--;
- assign_config_rcu(vport, new_mutable);
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))++;
-}
-
-static void port_table_remove_port(struct vport *vport)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-
- hlist_del_init_rcu(&tnl_vport->hash_node);
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))--;
-}
-
-static struct vport *port_table_lookup(struct port_lookup_key *key,
- const struct tnl_mutable_config **pmutable)
-{
- struct hlist_node *n;
- struct hlist_head *bucket;
- u32 hash = port_hash(key);
- struct tnl_vport *tnl_vport;
-
- bucket = find_bucket(hash);
-
- hlist_for_each_entry_rcu(tnl_vport, n, bucket, hash_node) {
- struct tnl_mutable_config *mutable;
-
- mutable = rcu_dereference_rtnl(tnl_vport->mutable);
- if (!memcmp(&mutable->key, key, PORT_KEY_LEN)) {
- *pmutable = mutable;
- return tnl_vport_to_vport(tnl_vport);
- }
- }
-
- return NULL;
-}
-
-struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
- __be64 key, int tunnel_type,
- const struct tnl_mutable_config **mutable)
-{
- struct port_lookup_key lookup;
- struct vport *vport;
- bool is_multicast = ipv4_is_multicast(saddr);
-
- port_key_set_net(&lookup, net);
- lookup.saddr = saddr;
- lookup.daddr = daddr;
-
- /* First try for exact match on in_key. */
- lookup.in_key = key;
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_EXACT;
- if (!is_multicast && key_local_remote_ports) {
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (key_remote_ports) {
- lookup.saddr = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
-
- lookup.saddr = saddr;
- }
-
- /* Then try matches that wildcard in_key. */
- lookup.in_key = 0;
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_MATCH;
- if (!is_multicast && local_remote_ports) {
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (remote_ports) {
- lookup.saddr = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
-
- if (is_multicast) {
- lookup.saddr = 0;
- lookup.daddr = saddr;
- if (key_multicast_ports) {
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_EXACT;
- lookup.in_key = key;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (multicast_ports) {
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_MATCH;
- lookup.in_key = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- }
-
- if (null_ports) {
- lookup.daddr = 0;
- lookup.saddr = 0;
- lookup.in_key = 0;
- lookup.tunnel_type = tunnel_type;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- return NULL;
-}
-
-static void ecn_decapsulate(struct sk_buff *skb)
-{
- if (unlikely(INET_ECN_is_ce(OVS_CB(skb)->tun_key->ipv4_tos))) {
- __be16 protocol = skb->protocol;
-
- skb_set_network_header(skb, ETH_HLEN);
-
- if (protocol == htons(ETH_P_8021Q)) {
- if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
- return;
-
- protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- skb_set_network_header(skb, VLAN_ETH_HLEN);
- }
-
- if (protocol == htons(ETH_P_IP)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct iphdr))))
- return;
-
- IP_ECN_set_ce(ip_hdr(skb));
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (protocol == htons(ETH_P_IPV6)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct ipv6hdr))))
- return;
-
- IP6_ECN_set_ce(ipv6_hdr(skb));
- }
-#endif
- }
-}
-