📄 tunnel.c
字号:
/* $Id: tunnel.c,v 1.87 2001/09/26 18:25:24 jm Exp $ * Tunnel interface functions * * Dynamic hierarchial IP tunnel * Copyright (C) 1998-2001, Dynamics group * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <sys/time.h>#include <unistd.h>#include <syslog.h>#include <assert.h>#include "tunnel.h"#include "hashtable.h"#include "debug.h"#include "list.h"#include "dyn_ip.h"#include "util.h"/* defines */#ifndef TRUE#define TRUE 1#endif#ifndef FALSE#define FALSE 0#endif#define DEBUG_FLAG 'T'#define PURGE_TUNNEL_TIME 3 /* seconds */#define LOG2(lev, fmt, args...) { DEBUG(DEBUG_FLAG, fmt, ## args); \ syslog(lev, fmt, ## args); }static char tunnel_dev_start[IFNAMSIZ];/* Maximum numbers of concurrent tunnels * This can be increased if needed, but Linux kernel (at least 2.2.3) seems * to take quite a long time, when the number of interfaces goes beyond * 2048. */#define MAX_TUNNEL_COUNT 2048#define MAX_TUNNEL_INTS (MAX_TUNNEL_COUNT >> 5)static unsigned int tunnel_numbers[MAX_TUNNEL_INTS];#define MAX_TABLE_COUNT 256#define MAX_TABLE_INTS (MAX_TABLE_COUNT >> 5)static unsigned int table_numbers[MAX_TABLE_INTS];#define DYN_SET_BIT(bits, bit) \ (bits[(bit) / 32] |= (1 << ((bit) & 0x1f)))#define DYN_CLR_BIT(bits, bit) \ (bits[(bit) / 32] &= ~(1 << ((bit) & 0x1f)))#define DYN_BIT(bits, bit) \ (bits[(bit) / 32] & (1 << ((bit) & 0x1f)))static voiddebug_print_bitfield(char *name, unsigned int bits[], int ints){#if 0 int i; DEBUG(DEBUG_FLAG, "%s[", name); for (i = 0; i < ints; i++) DEBUG(DEBUG_FLAG, "%08x", bits[i]); DEBUG(DEBUG_FLAG, "]\n");#endif}#define DELAY_UNCONNECTION_USEC 200000struct delayed_connection_deletion { struct delayed_connection_deletion *next; struct timeval timeout; /* when the connection can be deleted */ struct in_addr mn_addr; int reverse; int delete_route; int to_ha_table_id; int to_mn_table_id; char route_dev[IFNAMSIZ]; char reverse_dev[IFNAMSIZ];};/* priority queue (sorted by increasing timeout) of delayed connection * deletions */static struct delayed_connection_deletion *delayed = NULL;#define TUNNEL_HASH_SIZE 256static inthashfunc(void *key, const int hashsize){ __u8 hash; __u32 data; data = ((struct in_addr *) key)->s_addr; hash = (__u8)((data & 0xff000000) >> 24) ^ (__u8)((data & 0x00ff0000) >> 16) ^ (__u8)((data & 0x0000ff00) >> 8) ^ (__u8)(data & 0x000000ff); return ((int) hash);}/* * Comparison function for hash table * * Arguments: * key First data structure * cmprd Second data structure * * Return: * 0 Not equal * 1 Equal */static intcmpfunc(void *key, struct node *cmprd){ struct tunnel_key *data1; struct tunnel *data2; if (key == NULL) { DEBUG(DEBUG_FLAG, "tunnel module, cmpfunc:\n" "value for argument 'key' was NULL !\n" "Illegal value, returning 0 ! \n"); return 0; } if (cmprd == NULL) { DEBUG(DEBUG_FLAG, "tunnel module, cmpfunc:\n" "value for argument 'cmpd' was NULL !\n" "Illegal value, returning 0 ! \n"); return 0; } data1 = (struct tunnel_key *) key; data2 = (struct tunnel *) cmprd; /* Compare the tunnel destination addresses */ if (data1->dst_addr.s_addr == data2->dst_addr.s_addr && data1->type == data2->type && (data1->type == TUNNEL_IPIP || (data1->key == data2->key))) return 1; return 0;}static voidprint_tunnel_data(struct tunnel *data){ DEBUG(DEBUG_FLAG, "\tdevice=%s, num=%i, dst=%s, ref=%i\n", data->device, data->number, inet_ntoa(data->dst_addr), data->ref);}/** * tunnel_init: * @start_dev: The prefix to be used with tunnel device names, e.g., TUNL * @table_start: The first routing table that can be used, e.g., 1 * @table_end: The last routing table that can be used, e.g., 253 * * Initialize hash table for tunnels. Every tunnel has one entry * in the hash table. * * Returns: A pointer to the empty hash table or NULL on error */HASH *tunnel_init(char *start_dev, int table_start, int table_end){ int i; dynamics_strlcpy(tunnel_dev_start, start_dev, IFNAMSIZ); tunnel_dev_start[IFNAMSIZ - 1] = '\0'; for (i = 0; i < MAX_TUNNEL_INTS; i++) tunnel_numbers[i] = 0; /* only allow tables between table_start and table_end (including * both ends), i.e. mark other tables used */ for (i = 0; i < MAX_TABLE_INTS; i++) table_numbers[i] = 0; for (i = 0; i < table_start; i++) DYN_SET_BIT(table_numbers, i); for (i = table_end + 1; i < 256; i++) DYN_SET_BIT(table_numbers, i); debug_print_bitfield("TABLE_NUMBERS", table_numbers, MAX_TABLE_INTS); return (hashtable_init(TUNNEL_HASH_SIZE));}/* Destroy tunnel. Called from the iterator. This function removes the real * tunnel and frees the released resources. */static int tunnel_destroy(struct tunnel *data){ int ret; DEBUG(DEBUG_FLAG, "tunnel_destroy - dst=%s, dev=%s\n", inet_ntoa(data->dst_addr), data->device); if (data->direction == TUNNEL_UP && data->to_ha_table_id != -1) { DEBUG(DEBUG_FLAG, "Removing to_ha_table %i\n", data->to_ha_table_id); if (!DYN_BIT(table_numbers, data->to_ha_table_id)) LOG2(LOG_WARNING, "tunnel_destroy - to HA table already freed?\n"); DYN_CLR_BIT(table_numbers, data->to_ha_table_id); debug_print_bitfield("TABLE_NUMBERS", table_numbers, MAX_TABLE_INTS); /* remove the default route from the to_ha_table */ if (dyn_ip_route_del_table(data->device, data->to_ha_table_id, NULL) != 0) { LOG2(LOG_WARNING, "tunnel_destroy: could not remove " "default route from to_ha_table\n"); return -1; } } if (data->direction == TUNNEL_UP && data->to_mn_table_id != -1) { DEBUG(DEBUG_FLAG, "Removing to_mn_table %i\n", data->to_mn_table_id); if (dyn_ip_rule_del_table(NULL, NULL, data->to_mn_table_id, data->device) != 0) { LOG2(LOG_ALERT, "tunnel_destroy: could not remove rule" " to MN routing table (table=%i, dev=%s)\n", data->to_mn_table_id, data->device); } if (dyn_ip_route_del_blackhole(data->to_mn_table_id) != 0) { LOG2(LOG_ALERT, "tunnel_destroy: could not remove " "blackhole from MN routing table %i\n", data->to_mn_table_id); } if (!DYN_BIT(table_numbers, data->to_mn_table_id)) LOG2(LOG_WARNING, "tunnel_destroy - to MN table already freed?\n"); DYN_CLR_BIT(table_numbers, data->to_mn_table_id); debug_print_bitfield("TABLE_NUMBERS", table_numbers, MAX_TABLE_INTS); } ret = 1; /* remove the tunnel device */ DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_del(%s)\n", data->device); if (data->type != TUNNEL_NO_ENCAPS && dyn_ip_tunnel_del(data->device) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_del(%s) failed!\n", data->device); ret = -1; } if (ret != -1 && data->type != TUNNEL_NO_ENCAPS) { if (!DYN_BIT(tunnel_numbers, data->number)) { LOG2(LOG_WARNING, "tunnel_destroy: bit not set!?\n"); } else DYN_CLR_BIT(tunnel_numbers, data->number); debug_print_bitfield("TUNNEL_NUMBERS", tunnel_numbers, MAX_TUNNEL_INTS); } else if (data->type != TUNNEL_NO_ENCAPS) { LOG2(LOG_ALERT, "Tunnel #%i could not be released - keeping " "the number reserved\n", data->number); } hashtable_remove(&data->hashnode); free(data); return ret;}/* Iterator function for delayed tunnel deletion tunnel_check_delayed(). * tv = current time value or NULL for forced removal */static int check_tunnel(struct node *node, void *tv){ TUNNEL *data = (struct tunnel *) node; struct timeval *now; if (tv != NULL && (data->ref > 0 || data->do_not_remove)) return 1; now = (struct timeval *) tv; if (now == NULL || now->tv_sec < data->zero_ref_tstamp.tv_sec || now->tv_sec - data->zero_ref_tstamp.tv_sec > PURGE_TUNNEL_TIME) tunnel_destroy(data); return 1;}/** * tunnel_destroy_hash: * @hash: The pointer to the tunnel hash table * * Remove the possibly forgotten tunnel entries and destroy the hashtable. * * Returns 0 on success or 1 on error */inttunnel_destroy_hash(HASH *hash){ DEBUG(DEBUG_FLAG, "Removing delayed tunnel deletions..\n"); tunnel_check_delayed(hash, 1); if (hashtable_destroy(hash) == 0) return 1; else return 0;}/** * tunnel_add: * @hash: The pointer to the tunnel hashtable * @dst_addr: The remote address of the tunnel * @local_addr: The local address to be added to the tunnel. * @set_link_up: 1 = set the tunnel device up, * 0 = do not set the device up (caller takes care of it later) * @type: The type of the tunnel to be created * @key: The key of the tunnel to be created * @dev: If dev != NULL, the device name of the created or modified * tunnel will be copied to it. At least char[IFNAMSIZ]. * * Add or update an entry in the hash table. The entry is identified with the * source and destination addresses of the tunnel. The new tunnel is created * if it does not exist. This function does not change the routing tables. * * Returns: a pointer to the added/reused tunnel entry or NULL on failure */TUNNEL *tunnel_add(HASH *hash, struct in_addr dst_addr, char *dev, struct in_addr local_addr, int set_link_up, int type, int key){ TUNNEL *data, *new = NULL; char device[IFNAMSIZ]; int number = 0, ok; struct tunnel_key tkey; DEBUG(DEBUG_FLAG, "tunnel_add %s, type=%i, key=%i\n", inet_ntoa(dst_addr), type, key); tkey.dst_addr = dst_addr; tkey.type = type; tkey.key = key; data = (struct tunnel *) hashtable_fetch(hash, hashfunc, &tkey, cmpfunc); if (data != NULL) { /* tunnel already exists */ data->ref++; if (dev != NULL) dynamics_strlcpy(dev, data->device, IFNAMSIZ); DEBUG(DEBUG_FLAG, "\ttunnel already exists, %s (ref = %d)\n", inet_ntoa(dst_addr), data->ref); return data; } if (type == TUNNEL_NO_ENCAPS) { if (dyn_ip_get_ifname(key, device) < 0) { LOG2(LOG_WARNING, "tunnel_add - could not get ifname\n"); return NULL; } } else { /* create tunnel */ number = 0; while (number < MAX_TUNNEL_COUNT && DYN_BIT(tunnel_numbers, number)) number++; if (number >= MAX_TUNNEL_COUNT) { DEBUG(DEBUG_FLAG, "\tnot enough space for another " "tunnel (%i/%i)\n", number, MAX_TUNNEL_COUNT); return NULL; } snprintf(device, IFNAMSIZ, "%s%d", tunnel_dev_start, number); } if (type == TUNNEL_IPIP) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add(%s,%s,", device, inet_ntoa(dst_addr)); DEBUG(DEBUG_FLAG, "%s)\n", inet_ntoa(local_addr)); if (dyn_ip_tunnel_add(device, dst_addr, local_addr) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add failed!\n"); return NULL; } } else if (type == TUNNEL_GRE) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add_gre(%s,%s,", device, inet_ntoa(dst_addr)); DEBUG(DEBUG_FLAG, "%s,%i)\n", inet_ntoa(local_addr), key); if (dyn_ip_tunnel_add_gre(device, dst_addr, local_addr, key) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add_gre failed!\n"); return NULL; } } ok = TRUE; /* Add local address to the tunnel */ if (type != TUNNEL_NO_ENCAPS && dyn_ip_addr_add(device, local_addr) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_addr_add(%s, %s) failed!\n", device, inet_ntoa(local_addr)); ok = FALSE; } /* Tunnel device up */ if (type != TUNNEL_NO_ENCAPS && set_link_up && ok && dyn_ip_link_set_dev_up(device) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_link_set_dev_up(%s) failed!\n", device); ok = FALSE; } if (ok) { /* update the hashtable */ new = (struct tunnel *) calloc(1, sizeof(struct tunnel)); if (new == NULL) { DEBUG(DEBUG_FLAG, "\tmemory allocation failure!\n"); ok = FALSE; } } if (ok) { list_init_node(&new->hashnode); new->to_ha_table_id = -1; new->to_mn_table_id = -1; new->ref = 1; new->dst_addr = dst_addr; new->number = number; dynamics_strlcpy(new->device, device, IFNAMSIZ); new->type = type; new->key = key; new->direction = TUNNEL_NOT_CONNECTED; new->do_not_remove = 0; if (dev != NULL) dynamics_strlcpy(dev, device, IFNAMSIZ); } if (ok && hashtable_add(hash, hashfunc, &dst_addr, &new->hashnode) != TRUE) { DEBUG(DEBUG_FLAG, "\thashtable_add failed\n"); ok = FALSE; } if (!ok) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_del(%s)\n", device); if (dyn_ip_tunnel_del(device) != 0) DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_dev(%s) failed\n", device); if (new != NULL) free(new); return NULL; } /* okay, tunnel adding succeeded, update the bitfields etc. */ if (type != TUNNEL_NO_ENCAPS) { DYN_SET_BIT(tunnel_numbers, number); debug_print_bitfield("TUNNEL_NUMBERS", tunnel_numbers, MAX_TUNNEL_INTS); } return new;}/** * tunnel_set_remove: * @tunl: A tunnel entry * @remove: 0 = allow removal of unneeded tunnel, * 1 = deny removal of the tunnel even if reference count reaches * zero * * Set 'tunnel removing possible' flag for a tunnel entry. */void tunnel_set_remove(TUNNEL *tunl, int remove){ tunl->do_not_remove = remove;}/* Iterator function for tunnel_set_remove_all() */static int set_remove_iter(struct node *node, void *a){ TUNNEL *data = (struct tunnel *) node; struct in_addr *addr = (struct in_addr *) a; if (data->dst_addr.s_addr == addr->s_addr && data->do_not_remove) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -