/* $Id: listener.c,v 1.1 2001/04/10 01:58:42 csmall Exp $ * * Copyright (c) 1996 Jörg Reuter (jreuter@poboxes.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* TODO: Should add partial path to ax25_route if we are one of the * digipeaters. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NETAX25_AX25_H #include #else #include #endif #include "../pathnames.h" #include "ax25rtd.h" /* FIXME */ static unsigned long get_from_arp(unsigned char *data, int size) { if (size < 16) return 0; return ntohl(0); } static unsigned long get_from_ip(unsigned char *data, int size) { unsigned long adr; if ((*data & 0xf)*4 < IPLEN) return 0; adr = data[12] << 24; /* NETWORK byte order */ adr += data[13] << 16; adr += data[14] << 8; adr += data[15]; return ntohl(adr); /* HOST byte order */ } int call_is_mycall(config *config, ax25_address *call) { int k; for (k = 0; k < config->nmycalls; k++) if (!memcmp(call, &config->mycalls[k], AXLEN)) return 1; return 0; } /* * This catches *all* invalid callsigns, unlike ax25validate. */ static int check_ax25_addr(unsigned char *buf) { int k; unsigned char c; /* must start with at least one capital letter or digit */ for (k=0; k < 6; k++) { c = buf[k] >> 1; if (c == ' ') break; if ((c < 'A' || c > 'Z') && (c < '0' || c > '9')) return 1; } /* NULL address is invalid */ if (k == 0) return 1; /* remaining fields must consist of spaces only */ for (k++; k < 6; k++) if (buf[k] >> 1 != ' ') return 1; return 0; } static inline void invert_digipeater_path(ax25_address *digipeater, int ndigi) { int k, m; ax25_address fdigi; if (ndigi == 0) return; for (m = 0,k = ndigi-1; k > m; k--, m++) { memcpy(&fdigi, &digipeater[m], AXLEN); memcpy(&digipeater[m], &digipeater[k], AXLEN); memcpy(&digipeater[k], &fdigi, AXLEN); } } int set_arp(config *config, long ip, ax25_address *call) { struct sockaddr_in *isa; struct sockaddr_ax25 *asa; struct arpreq arp; int fds; if (!config->ip_add_arp) return 0; fds = socket(AF_INET, SOCK_DGRAM, 0); memset((char *) &arp, 0, sizeof(arp)); isa = (struct sockaddr_in *) &arp.arp_pa; asa = (struct sockaddr_ax25 *) &arp.arp_ha; isa->sin_family = AF_INET; isa->sin_port = 0; isa->sin_addr.s_addr = ip; asa->sax25_family = AF_AX25; asa->sax25_ndigis = 0; asa->sax25_call = *call; arp.arp_flags = ATF_PERM | ATF_COM; strcpy(arp.arp_dev, config->dev); if (ioctl(fds, SIOCSARP, &arp) < 0) { invalidate_ip_route(ip); perror("routspy: SIOCSARP"); close(fds); return 1; } close(fds); return 0; } int set_route(config *config, long ip) { struct rtentry rt; struct sockaddr_in *isa; char origdev[16], buf[1024]; /* modif f5lct */ long gwr; /* fin modif f5lct */ long ipr; int fds; FILE *fp; fp = fopen(PROC_IP_ROUTE_FILE, "r"); if (fp == NULL) { invalidate_ip_route(ip); return 1; } fgets(buf, sizeof(buf)-1, fp); /* discard header */ while (fgets(buf, sizeof(buf)-1, fp) != NULL) { /* modif f5lct */ /* sscanf(buf, "%s %lx", origdev, &ipr); */ sscanf(buf, "%s %lx %lx", origdev, &ipr, &gwr); if (ipr == ip && gwr == 00000000 ) /* fin modif f5lct */ { if (dev_get_config(origdev) == NULL) { invalidate_ip_route(ip); fclose(fp); return 1; } else { del_kernel_ip_route(origdev, ip); } } } fclose(fp); if (!config->ip_add_route) return 0; fds = socket(AF_INET, SOCK_DGRAM, 0); memset((char *) &rt, 0, sizeof(rt)); isa = (struct sockaddr_in *) &rt.rt_dst; isa->sin_family = AF_INET; isa->sin_port = 0; isa->sin_addr.s_addr = ip; rt.rt_flags = RTF_UP | RTF_HOST; rt.rt_dev = config->dev; if (config->tcp_irtt != 0) { rt.rt_irtt = config->tcp_irtt; rt.rt_flags |= RTF_IRTT; } isa = (struct sockaddr_in *) &rt.rt_genmask; isa->sin_addr.s_addr = 0xffffffff; if (ioctl(fds, SIOCADDRT, &rt) < 0) { invalidate_ip_route(ip); perror("ax25rtd: IP SIOCADDRT"); close(fds); return 1; } close(fds); return 0; } int del_kernel_ip_route(char *dev, long ip) { int fds; struct rtentry rt; struct sockaddr_in *isa; config *config; config = dev_get_config(dev); if (config == NULL || !config->ip_add_route) return 0; fds = socket(AF_INET, SOCK_DGRAM, 0); memset((char *) &rt, 0, sizeof(struct rtentry)); isa = (struct sockaddr_in *) &rt.rt_dst; isa->sin_family = AF_INET; isa->sin_addr.s_addr = ip; rt.rt_flags = RTF_UP | RTF_HOST; rt.rt_dev = dev; if (ioctl(fds, SIOCDELRT, &rt) < 0) { perror("ax25rtd: IP SIOCDELRT"); close(fds); return 1; } close(fds); return 0; } int set_ax25_route(config *config, ax25_rt_entry *rt) { struct ax25_routes_struct ax25_route; int fds, k; if (!config->ax25_add_route) return 0; ax25_route.port_addr = config->mycalls[0]; ax25_route.dest_addr = rt->call; ax25_route.digi_count = rt->ndigi; for (k = 0; k < rt->ndigi; k++) ax25_route.digi_addr[k] = rt->digipeater[k]; fds = socket(AF_AX25, SOCK_SEQPACKET, 0); if (ioctl(fds, SIOCADDRT, &ax25_route) < 0) { perror("ax25rtd: AX.25 SIOCADDRT"); close(fds); return 1; } close(fds); return 0; } int del_kernel_ax25_route(char *dev, ax25_address *call) { struct ax25_routes_struct ax25_route; int fds; config *config; config = dev_get_config(dev); if (config == NULL || !config->ax25_add_route) return 0; ax25_route.port_addr = config->mycalls[0]; ax25_route.dest_addr = *call; fds = socket(AF_AX25, SOCK_SEQPACKET, 0); if (ioctl(fds, SIOCDELRT, &ax25_route) < 0) { perror("ax25rtd: AX.25 SIOCDELRT"); close(fds); return 1; } close(fds); return 0; } int set_ipmode(config *config, ax25_address *call, int ipmode) { struct ax25_route_opt_struct ax25_opt; int fds; if (!config->ip_adjust_mode) return 0; ax25_opt.port_addr = config->mycalls[0]; ax25_opt.dest_addr = *call; ax25_opt.cmd = AX25_SET_RT_IPMODE; ax25_opt.arg = ipmode? 'V':'D'; fds = socket(AF_AX25, SOCK_SEQPACKET, 0); if (ioctl(fds, SIOCAX25OPTRT, &ax25_opt) < 0) { perror("ax25rtd: SIOCAX25OPTRT"); close(fds); return 1; } close(fds); return 0; } /* Yes, the code *IS* ugly... */ #define SKIP(o) {data+=(o); size-=(o);} void ax25_receive(int sock) { unsigned char buf[1500]; unsigned char *data; unsigned long ip; struct sockaddr sa; ax25_address srccall, destcall, digipeater[AX25_MAX_DIGIS]; char extseq = 0; int size, asize, action, ipmode, ctl, pid, ndigi, kdigi, mine; time_t stamp; config *config; ax25_rt_entry * ax25rt; asize = sizeof(sa); if ((size = recvfrom(sock, buf, sizeof(buf), 0, &sa, &asize)) < 0) { perror("recvfrom"); save_cache(); daemon_shutdown(1); } stamp = time(NULL); ip = 0; pid = ctl = 0; config = dev_get_config(sa.sa_data); if (config == NULL) return; data = buf; /* * KISS data? */ if ((*data & 0x0f) != 0) return; SKIP(1); /* valid frame? */ if (size < (2*AXLEN+1)) return; /* * Get destination callsign */ if (check_ax25_addr(data)) return; memcpy(&destcall, data, AXLEN); destcall.ax25_call[6] &= 0x1e; SKIP(AXLEN); mine = call_is_mycall(config, &destcall); /* * Get Source callsign */ if (check_ax25_addr(data)) return; memcpy(&srccall, data, AXLEN); srccall.ax25_call[6] &= 0x1e; SKIP(ALEN); /* * How long is our control field? */ extseq = ~(*data) & SSSID_SPARE; /* * Extract digipeaters */ ndigi = 0; while (((*data) & HDLCAEB) != HDLCAEB) { SKIP(1); if (size <= 0 || check_ax25_addr(data)) return; if (ndigi < AX25_MAX_DIGIS) memcpy(&digipeater[ndigi++], data, AXLEN); else return; SKIP(ALEN); } SKIP(1); if (size <= 0) return; /* * Get type of frame */ if ((*data & LAPB_S) == LAPB_I) ctl = LAPB_I; else { ctl = *data; if (extseq == 0) ctl &= ~LAPB_PF; } /* * Check if info frame and get PID */ if (ctl == LAPB_I || ctl == LAPB_UI) { SKIP(extseq? 2:1); if (size <= 0) return; /* Get PID */ pid = *data; if (pid == PID_SEGMENT) { SKIP(1); if (size <= 0) return; pid = 0; if (*data && SEG_FIRST) { pid = *data; SKIP(1); if (size <= 0) return; } } } /* * See if it is fully digipeated (TODO: or if we are the next digipeater) */ for (kdigi = 0; kdigi < ndigi; kdigi++) { if ((digipeater[kdigi].ax25_call[6] & AX25_REPEATED) != AX25_REPEATED) return; digipeater[kdigi].ax25_call[6] &= 0x1e; } invert_digipeater_path(digipeater, ndigi); /* * Are we allowed to add it to our routing table? */ if (mine || !config->ax25_for_me) { if (!mine && ndigi == 0 && config->ax25_add_default) { ndigi = config->ax25_default_path.fsa_ax25.sax25_ndigis; for (kdigi = 0; kdigi < ndigi; kdigi++) if (!memcmp(&srccall, &config->ax25_default_path.fsa_digipeater[kdigi], AXLEN)) break; if (ndigi == kdigi) memcpy(digipeater, config->ax25_default_path.fsa_digipeater, ndigi*AXLEN); else ndigi = 0; } ax25rt = update_ax25_route(config, &srccall, ndigi, digipeater, stamp); if (ax25rt != NULL) set_ax25_route(config, ax25rt); } /* * Now see if it carries IP traffic */ switch(pid) { case PID_ARP: SKIP(1); if (size > 0) ip = get_from_arp(data, size); break; case PID_IP: if (!mine) return; SKIP(1); if (size > 0) ip = get_from_ip(data, size); break; default: return; } /* * And adjust routes/arp/ipmode if we are allowed to... */ ipmode = (ctl == LAPB_I); if (ip != 0) { if ((config = dev_get_config(ip_encaps_dev)) == NULL) return; action = update_ip_route(config, ip, ipmode, &srccall, stamp); if (action & NEW_ROUTE) if (set_route(config, ip)) return; if (action & NEW_ARP) if (set_arp(config, ip, &srccall)) return; if (action & NEW_IPMODE) if (set_ipmode(config, &srccall, ipmode)) return; } }