-struct mutable_config {
- struct rcu_head rcu;
-
- unsigned char eth_addr[ETH_ALEN];
- unsigned int mtu;
- struct gre_port_config port_config;
-
- int tunnel_hlen; /* Tunnel header length. */
-};
-
-struct gre_vport {
- struct rcu_head rcu;
- struct tbl_node tbl_node;
-
- char name[IFNAMSIZ];
-
- /* Protected by RCU. */
- struct mutable_config *mutable;
-};
-
-/* Protected by RCU. */
-static struct tbl *port_table;
-
-/* These are just used as an optimization: they don't require any kind of
- * synchronization because we could have just as easily read the value before
- * the port change happened. */
-static unsigned int key_local_remote_ports;
-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)
-{
- return vport_priv(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)
-{
- return container_of(node, struct gre_vport, tbl_node);
-}
-
-/* RCU callback. */
-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)
-{
- struct gre_vport *gre_vport = gre_vport_priv(vport);
- struct mutable_config *old_config;
-
- old_config = rcu_dereference(gre_vport->mutable);
- rcu_assign_pointer(gre_vport->mutable, new_config);
- call_rcu(&old_config->rcu, free_config);
-}
-
-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)
- return &local_remote_ports;
- else
- return &remote_ports;
- } else {
- if (mutable->port_config.saddr)
- return &key_local_remote_ports;
- else
- return &key_remote_ports;
- }
-}
-
-enum lookup_key {
- LOOKUP_SADDR = 0,
- LOOKUP_DADDR = 1,
- LOOKUP_KEY = 2,
- LOOKUP_KEY_MATCH = 3
-};
-
-struct port_lookup_key {
- u32 vals[4]; /* Contains enum lookup_key keys. */
- const struct mutable_config *mutable;
-};
-
-/* 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)
-{
- const struct gre_vport *gre_vport = gre_vport_table_cast(node);
- struct port_lookup_key *lookup = target;
-
- lookup->mutable = rcu_dereference(gre_vport->mutable);
-
- return ((lookup->mutable->port_config.flags & GRE_F_IN_KEY_MATCH) ==
- lookup->vals[LOOKUP_KEY_MATCH]) &&
- lookup->mutable->port_config.daddr == lookup->vals[LOOKUP_DADDR] &&
- lookup->mutable->port_config.in_key == lookup->vals[LOOKUP_KEY] &&
- lookup->mutable->port_config.saddr == lookup->vals[LOOKUP_SADDR];
-}
-
-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)
-{
- struct gre_vport *gre_vport = gre_vport_priv(vport);
- struct port_lookup_key lookup;
- int err;
-
- if (!port_table) {
- struct tbl *new_table;
-
- new_table = tbl_create(0);
- if (!new_table)
- return -ENOMEM;
-
- rcu_assign_pointer(port_table, new_table);
-
- } else if (tbl_count(port_table) > tbl_n_buckets(port_table)) {
- struct tbl *old_table = port_table;
- struct tbl *new_table;
-
- new_table = tbl_expand(old_table);
- if (IS_ERR(new_table))
- return PTR_ERR(new_table);
-
- rcu_assign_pointer(port_table, new_table);
- tbl_deferred_destroy(old_table, NULL);
- }
-
- lookup.vals[LOOKUP_SADDR] = gre_vport->mutable->port_config.saddr;
- lookup.vals[LOOKUP_DADDR] = gre_vport->mutable->port_config.daddr;
- lookup.vals[LOOKUP_KEY] = gre_vport->mutable->port_config.in_key;
- lookup.vals[LOOKUP_KEY_MATCH] = gre_vport->mutable->port_config.flags & GRE_F_IN_KEY_MATCH;
-
- err = tbl_insert(port_table, &gre_vport->tbl_node, port_hash(&lookup));
- if (err)
- return err;
-
- (*find_port_pool(gre_vport->mutable))++;
-
- return 0;
-}
-
-static int del_port(struct vport *vport)
-{
- struct gre_vport *gre_vport = gre_vport_priv(vport);
- int err;
-
- err = tbl_remove(port_table, &gre_vport->tbl_node);
- if (err)
- return err;
-
- (*find_port_pool(gre_vport->mutable))--;
-
- return 0;
-}
-
-#define FIND_PORT_KEY (1 << 0)
-#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)
-{
- struct port_lookup_key lookup;
- struct tbl *table = rcu_dereference(port_table);
- struct tbl_node *tbl_node;
-
- if (!table)
- return NULL;
-
- lookup.vals[LOOKUP_SADDR] = saddr;
- lookup.vals[LOOKUP_DADDR] = daddr;
-
- if (port_type & FIND_PORT_KEY) {
- lookup.vals[LOOKUP_KEY] = key;
- lookup.vals[LOOKUP_KEY_MATCH] = 0;
-
- if (key_local_remote_ports) {
- tbl_node = tbl_lookup(table, &lookup, port_hash(&lookup), port_cmp);
- if (tbl_node)
- goto found;
- }
-
- if (key_remote_ports) {
- lookup.vals[LOOKUP_SADDR] = 0;
-
- tbl_node = tbl_lookup(table, &lookup, port_hash(&lookup), port_cmp);
- if (tbl_node)
- goto found;
-
- lookup.vals[LOOKUP_SADDR] = saddr;
- }
- }
-
- if (port_type & FIND_PORT_MATCH) {
- lookup.vals[LOOKUP_KEY] = 0;
- lookup.vals[LOOKUP_KEY_MATCH] = GRE_F_IN_KEY_MATCH;
-
- if (local_remote_ports) {
- tbl_node = tbl_lookup(table, &lookup, port_hash(&lookup), port_cmp);
- if (tbl_node)
- goto found;
- }
-
- if (remote_ports) {
- lookup.vals[LOOKUP_SADDR] = 0;
-
- tbl_node = tbl_lookup(table, &lookup, port_hash(&lookup), port_cmp);
- if (tbl_node)
- goto found;
- }
- }
-
- return NULL;
-
-found:
- *mutable = lookup.mutable;
- return gre_vport_to_vport(gre_vport_table_cast(tbl_node));
-}
-
-static bool check_ipv4_address(__be32 addr)