X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/5e01d2924d4eec8915e93a2cae01217ae25ab4dc..6d5c3ecb4d12ca63e4f9f809255691b3b2cda6e1:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index facecab..88a62a5 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.51 2004-11-17 08:23:34 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.58 2004-11-28 02:53:11 bodea Exp $"; #include #include @@ -54,7 +54,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.51 2004-11-17 08:23:34 bodea Exp #endif /* BGP */ // Globals -struct configt *config = NULL; // all configuration +configt *config = NULL; // all configuration int tunfd = -1; // tun interface file handle. (network device) int udpfd = -1; // UDP file handle int controlfd = -1; // Control signal handle @@ -88,9 +88,9 @@ linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; #define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER) -#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE } +#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(configt, MEMBER), membersize(configt, MEMBER), TYPE } -struct config_descriptt config_values[] = { +config_descriptt config_values[] = { CONFIG("debug", debug, INT), CONFIG("log_file", log_filename, STRING), CONFIG("pid_file", pid_file, STRING), @@ -146,6 +146,7 @@ sessiont *session = NULL; // Array of session structures. sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters. radiust *radius = NULL; // Array of radius structures. ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. +ip_filtert *ip_filters = NULL; // Array of named filters. static controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER @@ -657,6 +658,7 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t) STAT(tunnel_tx_errors); return; } + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; *(u32 *) & addr.sin_addr = htonl(tunnel[t].ip); @@ -783,11 +785,7 @@ static void processipout(u8 * buf, int len) // Add on L2TP header { u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); - if (!p) - { - LOG(3, session[s].ip, s, t, "failed to send packet in processipout.\n"); - return; - } + if (!p) return; tunnelsend(b, len + (p-b), t); // send it... } @@ -837,11 +835,7 @@ static void send_ipout(sessionidt s, u8 *buf, int len) // Add on L2TP header { u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); - if (!p) - { - LOG(3, session[s].ip, s, t, "failed to send packet in send_ipout.\n"); - return; - } + if (!p) return; tunnelsend(b, len + (p-b), t); // send it... } @@ -949,8 +943,10 @@ static void controladd(controlt * c, tunnelidt t, sessionidt s) tunnel[t].controle->next = c; else tunnel[t].controls = c; + tunnel[t].controle = c; tunnel[t].controlc++; + // send now if space in window if (tunnel[t].controlc <= tunnel[t].window) { @@ -1006,6 +1002,44 @@ void throttle_session(sessionidt s, int rate_in, int rate_out) } } +// add/remove filters from session (-1 = no change) +void filter_session(sessionidt s, int filter_in, int filter_out) +{ + if (!session[s].tunnel) + return; // No-one home. + + if (!*session[s].user) + return; // User not logged in + + // paranoia + if (filter_in > MAXFILTER) filter_in = -1; + if (filter_out > MAXFILTER) filter_out = -1; + if (session[s].filter_in > MAXFILTER) session[s].filter_in = 0; + if (session[s].filter_out > MAXFILTER) session[s].filter_out = 0; + + if (filter_in >= 0) + { + if (session[s].filter_in) + ip_filters[session[s].filter_in - 1].used--; + + if (filter_in > 0) + ip_filters[filter_in - 1].used++; + + session[s].filter_in = filter_in; + } + + if (filter_out >= 0) + { + if (session[s].filter_out) + ip_filters[session[s].filter_out - 1].used--; + + if (filter_out > 0) + ip_filters[filter_out - 1].used++; + + session[s].filter_out = filter_out; + } +} + // start tidy shutdown of session void sessionshutdown(sessionidt s, char *reason) { @@ -1052,15 +1086,20 @@ void sessionshutdown(sessionidt s, char *reason) if (session[s].ip) { // IP allocated, clear and unroute int r; + int routed = 0; for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) { + if ((session[s].ip & session[s].route[r].mask) == + (session[s].route[r].ip & session[s].route[r].mask)) + routed++; + routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0); session[s].route[r].ip = 0; } if (session[s].ip_pool_index == -1) // static ip { - routeset(s, session[s].ip, 0, 0, 0); // Delete route. + if (!routed) routeset(s, session[s].ip, 0, 0, 0); session[s].ip = 0; } else @@ -1080,6 +1119,10 @@ void sessionshutdown(sessionidt s, char *reason) if (!session[s].die) session[s].die = now() + 150; // Clean up in 15 seconds + // update filter refcounts + if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; + if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; + cluster_send_session(s); } @@ -1109,11 +1152,7 @@ void sendipcp(tunnelidt t, sessionidt s) } q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPCP); - if (!q) - { - LOG(3, session[s].ip, s, t, "failed to send packet in sendipcp.\n"); - return; - } + if (!q) return; *q = ConfigReq; q[1] = r << RADIUS_SHIFT; // ID, dont care, we only send one type of request @@ -1312,10 +1351,9 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) STAT(tunnel_rx_errors); return; } - LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n", - l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); - // if no tunnel specified, assign one - if (!t) + + // check for duplicate tunnel open message + if (!t && ns == 0) { int i; @@ -1329,10 +1367,15 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) tunnel[i].port != ntohs(addr->sin_port) ) continue; t = i; + LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Duplicate SCCRQ?\n"); break; } } + LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n", + l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); + + // if no tunnel specified, assign one if (!t) { if (!(t = new_tunnel())) @@ -1345,8 +1388,28 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr); tunnel[t].port = ntohs(addr->sin_port); tunnel[t].window = 4; // default window - LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n", tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t); STAT(tunnel_created); + LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n", + tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, + tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t); + } + + // If the 'ns' just received is not the 'nr' we're + // expecting, just send an ack and drop it. + // + // if 'ns' is less, then we got a retransmitted packet. + // if 'ns' is greater than missed a packet. Either way + // we should ignore it. + if (ns != tunnel[t].nr) + { + // is this the sequence we were expecting? + STAT(tunnel_rx_errors); + LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n", + t, ns, tunnel[t].nr); + + if (l) // Is this not a ZLB? + controlnull(t); + return; } // This is used to time out old tunnels @@ -1356,7 +1419,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) { int skip = tunnel[t].window; // track how many in-window packets are still in queue // some to clear maybe? - while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) + while (tunnel[t].controlc > 0 && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) { controlt *c = tunnel[t].controls; tunnel[t].controls = c->next; @@ -1367,22 +1430,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) tunnel[t].try = 0; // we have progress } - // If the 'ns' just received is not the 'nr' we're - // expecting, just send an ack and drop it. - // - // if 'ns' is less, then we got a retransmitted packet. - // if 'ns' is greater than missed a packet. Either way - // we should ignore it. - if (ns != tunnel[t].nr) - { - // is this the sequence we were expecting? - LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n", t, ns, tunnel[t].nr); - STAT(tunnel_rx_errors); - - if (l) // Is this not a ZLB? - controlnull(t); - return; - } // receiver advance (do here so quoted correctly in any sends below) if (l) tunnel[t].nr = (ns + 1); if (skip < 0) skip = 0; @@ -2067,11 +2114,7 @@ static int regular_cleanups(void) u8 b[MAXCONTROL] = {0}; u8 *q = makeppp(b, sizeof(b), 0, 0, session[s].tunnel, s, PPPLCP); - if (!q) - { - LOG(3, session[s].ip, s, t, "failed to send ECHO packet.\n"); - continue; - } + if (!q) continue; *q = EchoReq; *(u8 *)(q + 1) = (time_now % 255); // ID @@ -2130,6 +2173,22 @@ static int regular_cleanups(void) send++; } + if (a & CLI_SESS_NOFILTER) + { + LOG(2, 0, s, session[s].tunnel, "Un-filtering session by CLI\n"); + filter_session(s, 0, 0); + send++; + } + else if (a & CLI_SESS_FILTER) + { + LOG(2, 0, s, session[s].tunnel, "Filtering session by CLI (in=%d, out=%d)\n", + cli_session_actions[s].filter_in, + cli_session_actions[s].filter_out); + + filter_session(s, cli_session_actions[s].filter_in, cli_session_actions[s].filter_out); + send++; + } + if (send) cluster_send_session(s); @@ -2490,12 +2549,12 @@ static void initdata(int optdebug, char *optconfig) LOG(0, 0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno)); exit(1); } - if (!(config = shared_malloc(sizeof(struct configt)))) + if (!(config = shared_malloc(sizeof(configt)))) { LOG(0, 0, 0, 0, "Error doing malloc for configuration: %s\n", strerror(errno)); exit(1); } - memset(config, 0, sizeof(struct configt)); + memset(config, 0, sizeof(configt)); time(&config->start_time); strncpy(config->config_file, optconfig, strlen(optconfig)); config->debug = optdebug; @@ -2531,6 +2590,13 @@ static void initdata(int optdebug, char *optconfig) exit(1); } +if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER))) +{ + LOG(0, 0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno)); + exit(1); +} +memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); + #ifdef RINGBUFFER if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer)))) { @@ -2582,11 +2648,11 @@ static void initdata(int optdebug, char *optconfig) _statistics->start_time = _statistics->last_reset = time(NULL); #ifdef BGP - if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS))) - { - LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno)); - exit(1); - } + if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS))) + { + LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno)); + exit(1); + } #endif /* BGP */ } @@ -3576,18 +3642,30 @@ int sessionsetup(tunnelidt t, sessionidt s) } } + { + int routed = 0; + // Add the route for this session. - // - // Static IPs need to be routed. Anything else - // is part of the IP address pool and is already routed, - // it just needs to be added to the IP cache. - if (session[s].ip_pool_index == -1) // static ip - routeset(s, session[s].ip, 0, 0, 1); - else - cache_ipmap(session[s].ip, s); + for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) + { + if ((session[s].ip & session[s].route[r].mask) == + (session[s].route[r].ip & session[s].route[r].mask)) + routed++; + + routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 1); + } - for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) - routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 1); + // Static IPs need to be routed if not already + // convered by a Framed-Route. Anything else is part + // of the IP address pool and is already routed, it + // just needs to be added to the IP cache. + if (session[s].ip_pool_index == -1) // static ip + { + if (!routed) routeset(s, session[s].ip, 0, 0, 1); + } + else + cache_ipmap(session[s].ip, s); + } if (!session[s].unique_id) { @@ -3631,6 +3709,7 @@ int sessionsetup(tunnelidt t, sessionidt s) int load_session(sessionidt s, sessiont *new) { int i; + int newip = 0; // Sanity checks. if (new->ip_pool_index >= MAXIPPOOL || @@ -3648,46 +3727,89 @@ int load_session(sessionidt s, sessiont *new) session[s].tunnel = new->tunnel; // For logging in cache_ipmap + // See if routes/ip cache need updating + if (new->ip != session[s].ip) + newip++; - if (new->ip != session[s].ip) // Changed ip. fix up hash tables. + for (i = 0; !newip && i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++) + if (new->route[i].ip != session[s].route[i].ip || + new->route[i].mask != session[s].route[i].mask) + newip++; + + // needs update + if (newip) { - if (session[s].ip) // If there's an old one, remove it. + int routed = 0; + + // remove old routes... + for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++) { - // Remove any routes if the IP has changed - for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++) - { - routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 0); - session[s].route[i].ip = 0; - } + if ((session[s].ip & session[s].route[i].mask) == + (session[s].route[i].ip & session[s].route[i].mask)) + routed++; + + routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 0); + } + // ...ip + if (session[s].ip) + { if (session[s].ip_pool_index == -1) // static IP - routeset(s, session[s].ip, 0, 0, 0); + { + if (!routed) routeset(s, session[s].ip, 0, 0, 0); + } else // It's part of the IP pool, remove it manually. uncache_ipmap(session[s].ip); } + routed = 0; + + // add new routes... + for (i = 0; i < MAXROUTE && new->route[i].ip; i++) + { + if ((new->ip & new->route[i].mask) == + (new->route[i].ip & new->route[i].mask)) + routed++; + + routeset(s, new->route[i].ip, new->route[i].mask, 0, 1); + } + + // ...ip if (new->ip) { // If there's a new one, add it. if (new->ip_pool_index == -1) - routeset(s, new->ip, 0, 0, 1); + { + if (!routed) routeset(s, new->ip, 0, 0, 1); + } else cache_ipmap(new->ip, s); } } - // Update routed networks - for (i = 0; i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++) + // check filters + if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0])) { - if (new->route[i].ip == session[s].route[i].ip && - new->route[i].mask == session[s].route[i].mask) - continue; + LOG(2, session[s].ip, s, session[s].tunnel, "Dropping invalid input filter %d\n", (int) new->filter_in); + new->filter_in = 0; + } - if (session[s].route[i].ip) // Remove the old one if it exists. - routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 0); + if (new->filter_out && (new->filter_out > MAXFILTER || !ip_filters[new->filter_out - 1].name[0])) + { + LOG(2, session[s].ip, s, session[s].tunnel, "Dropping invalid output filter %d\n", (int) new->filter_out); + new->filter_out = 0; + } - if (new->route[i].ip) // Add the new one if it exists. - routeset(s, new->route[i].ip, new->route[i].mask, 0, 1); + if (new->filter_in != session[s].filter_in) + { + if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; + if (new->filter_in) ip_filters[new->filter_in - 1].used++; + } + + if (new->filter_out != session[s].filter_out) + { + if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; + if (new->filter_out) ip_filters[new->filter_out - 1].used++; } if (new->tunnel && s > config->cluster_highest_sessionid) // Maintain this in the slave. It's used @@ -3760,10 +3882,12 @@ static int add_plugin(char *plugin_name) sessionbyuser, sessiontbysessionidt, sessionidtbysessiont, - sessionkill, radiusnew, radiussend, getconfig, + sessionkill, + throttle_session, + cluster_send_session, }; void *p = open_plugin(plugin_name, 1); @@ -3857,17 +3981,20 @@ static int remove_plugin(char *plugin_name) int run_plugins(int plugin_type, void *data) { int (*func)(void *data); - if (!plugins[plugin_type] || plugin_type > max_plugin_functions) return 1; + + if (!plugins[plugin_type] || plugin_type > max_plugin_functions) + return PLUGIN_RET_ERROR; ll_reset(plugins[plugin_type]); while ((func = ll_next(plugins[plugin_type]))) { - int rc; - rc = func(data); - if (rc == PLUGIN_RET_STOP) return 1; - if (rc == PLUGIN_RET_ERROR) return 0; + int r = func(data); + + if (r != PLUGIN_RET_OK) + return r; // stop here } - return 1; + + return PLUGIN_RET_OK; } static void plugins_done() @@ -3964,8 +4091,17 @@ static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen case NSCTL_REQ_CONTROL: { - struct param_control param = { request.argc, request.argv, 0, NULL }; - if (!run_plugins(PLUGIN_CONTROL, ¶m)) + struct param_control param = { + config->cluster_iam_master, + request.argc, + request.argv, + 0, + NULL, + }; + + int r = run_plugins(PLUGIN_CONTROL, ¶m); + + if (r == PLUGIN_RET_ERROR) { response.type = NSCTL_RES_ERR; response.argc = 1; @@ -3973,6 +4109,22 @@ static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen ? param.additional : "error returned by plugin"; } + else if (r == PLUGIN_RET_NOTMASTER) + { + static char msg[] = "must be run on master: 000.000.000.000"; + + response.type = NSCTL_RES_ERR; + response.argc = 1; + if (config->cluster_master_address) + { + strcpy(msg + 23, inet_toa(config->cluster_master_address)); + response.argv[0] = msg; + } + else + { + response.argv[0] = "must be run on master: none elected"; + } + } else if (!(param.response & NSCTL_RESPONSE)) { response.type = NSCTL_RES_ERR;