/* IPX routing daemon Copyright (C) 1994, 1995 Ales Dryak Copyright (C) 1996, Volker Lendecke 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. */ /* done: filter my broadcasts for proper aging */ /* done: age each iface separately */ /* done: DONTROUTE ioctl */ /* done: shutdown */ /* done: logovani ... */ /* done: full daemonize */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipxutil.h" #include "ipxkern.h" #include "ipxrip.h" #include "ipxd.h" typedef unsigned char ifc_timer; struct rip_table { IPXNet network; hop_t hops; tick_t ticks; IPXNode router_node; struct ipx_interface *router_interface; ifc_timer timers[MAX_IFACE]; /* 0 net is reachable via iface */ /* timer>=EXPIPE_TIME => net is not r. */ struct rip_table* next; }; struct rip_table *rtable = NULL; static IPXNet ifc_net(struct ipx_interface *ifc) { return ntohl(ifc->r_output.dest_addr.sipx_network); } static IPXNet rt_net(struct rip_table *rt) { return ifc_net(rt->router_interface); } /* get some ipx socket */ static int ipx_socket(void) { struct ipx_interface *ifc = first_interface(); if (first_interface() == NULL) { LOG_START; fprintf(log_file, "No ipx socket available\n"); LOG_END; exit(1); } return ifc->r_output.sk; } static int is_expired(ifc_timer* tm) { return *tm>=EXPIRE_TIME; } /* Do I forward packets targeted to rt->network coming over interface * ifc? */ static int is_rt_target(struct rip_table *rt, struct ipx_interface *ifc) { return is_expired(rt->timers + ifc_get_index(ifc)); } /* set up the timers in rt so that only ifc is reachable. */ static void setup_timers(struct rip_table* rt, struct ipx_interface* ifc) { ifc_timer* tm; for(tm=rt->timers; tm < rt->timers+MAX_IFACE; tm += 1) { *tm=EXPIRE_TIME; } rt->timers[ifc_get_index(ifc)]=0; } static void output_flushall() { struct ipx_interface *ifc = first_interface(); while (ifc != NULL) { ipx_rip_output_flush(&(ifc->r_output)); ifc = next_interface(ifc); } } static void output_set_destination(struct ipx_interface *ifc, IPXNode dest_node, IPXPort dest_port) { if (ifc == NULL) { ifc = first_interface(); while (ifc != NULL) { ipx_rip_output_set_destination(&(ifc->r_output), dest_node, dest_port); ifc = next_interface(ifc); } } else { ipx_rip_output_set_destination(&(ifc->r_output), dest_node, dest_port); } } /* Broadcast the existence of the route rt to all interfaces through * which rt's target is not directly reachable. */ static void output_broadcast(struct rip_table *rt, int down_allow) { struct ipx_interface *ifc = first_interface(); while (ifc != NULL) { if (is_expired(&(rt->timers[ifc_get_index(ifc)]))) { ipx_rip_output_response(&(ifc->r_output),rt->network, rt->hops,rt->ticks,down_allow); } ifc = next_interface(ifc); } } static int output_sendto(int sock, void *buffer, int size, struct sockaddr_ipx *daddr) { int res; DL_ENTRY; DL_START; fprintf(log_file,"Sending RIP to "); ipx_fprint_saddr(log_file,daddr); fprintf(log_file,"\n"); ipx_rip_fdump(log_file,buffer,size); DL_END; res=sendto(sock,(void*)buffer,size,0, (struct sockaddr*)daddr,sizeof(*daddr)); if (res==-1) { LOG_START; fprintf(log_file,"sendto: %s\n",strerror(errno)); LOG_END; } return res; } static void fprint_route(FILE *file, struct rip_table *rt) { int i; LOG_START; fprintf(file,"dst:"); ipx_fprint_network(file,rt->network); fprintf(file," h:%d t:%d rtr:",rt->hops,rt->ticks); ipx_fprint_network(file,rt_net(rt)); fprintf(file,":"); ipx_fprint_node(file,rt->router_node); fprintf(file," "); for (i = first_ifc_index(); i >= 0; i = next_ifc_index(i)) { ifc_timer* tm = &(rt->timers[i]);; fprintf(file,"%i",is_expired(tm)?0:1); DL_START; fprintf(file,"(%i)",(int)*tm); DL_END; } LOG_END; } void fdump_routes(FILE *file) { struct rip_table *rt; LOG_START; fprintf(file,"IPX routing database:\n"); for(rt=rtable; rt!=NULL; rt=rt->next) { fprint_route(file,rt); fprintf(file,"\n"); } LOG_END; } static struct rip_table* add_route(IPXNet to, hop_t h, tick_t t, struct ipx_interface *ifc, IPXNode rt_node) { struct rip_table *rt; rt=(struct rip_table*)malloc(sizeof(struct rip_table)); if (rt == NULL) { LOG_START; fprintf(log_file,"ipxripd: out of memory in add_route\n"); LOG_END; return NULL; } rt->network=to; rt->hops=h; rt->ticks=t; rt->router_interface=ifc; ipx_assign_node(rt->router_node,rt_node); setup_timers(rt,ifc); rt->next=rtable; rtable=rt; return rt; } static void delete_route(struct rip_table* d) { struct rip_table **r; for(r=&rtable; *r!=NULL; r=&((*r)->next)) { if (*r==d) { *r=d->next; free(d); return; } } } static void delete_invalid_routes(void) { struct rip_table **r = &rtable; while (*r != NULL) { if ((*r)->hops >= IPX_RIP_NET_DOWN) { struct rip_table *d = *r; *r = (*r)->next; free(d); } else { r = &((*r)->next); } } } static void ipx_rip_rt_add(struct rip_table *rt) { if(ipx_kern_route_add(ipx_socket(), rt->network, rt_net(rt), rt->router_node) < 0) { LOG_START; fprintf(log_file,"Cannot add route to network "); ipx_fprint_network(log_file,rt->network); fprintf(log_file,"\n%s\n",ipx_err_string); LOG_END; } } static void ipx_rip_rt_del(IPXNet net) { if (ipx_kern_route_delete(ipx_socket(), net)<0) { LOG_START; fprintf(log_file,"Cannot delete route to network "); ipx_fprint_network(log_file,net); fprintf(log_file,"\n%s\n",ipx_err_string); LOG_END; } } static void handle_rip_request(struct rip_entry *re, struct ipx_interface *src_ifc) { struct rip_table *cur; if (re->network==htonl(IPX_RIP_GENERAL_RQ)) { for(cur=rtable; cur!=NULL; cur=cur->next) { if (is_rt_target(cur, src_ifc)) { ipx_rip_output_response(&(src_ifc->r_output), cur->network, cur->hops, cur->ticks,0); } } } else { for(cur=rtable; cur!=NULL; cur=cur->next) { if ( (htonl(re->network)==cur->network) && ( (is_rt_target(cur, src_ifc)) || (ifc_net(src_ifc)==htonl(re->network)))) { ipx_rip_output_response(&(src_ifc->r_output), cur->network, cur->hops, cur->ticks,0); } } } } static void handle_rip_response(struct rip_entry *re, struct ipx_interface *src_ifc, IPXNode src_node) { struct rip_table *cur; re->hops=ntohs(re->hops)+1; re->ticks=ntohs(re->ticks)+src_ifc->ticks; for(cur=rtable; cur!=NULL; cur=cur->next) { if (cur->network != htonl(re->network)) { continue; } if (cur->network==rt_net(cur)) { /* don't change routes to ifaces */ return; } if (re->hops <= IPX_RIP_NET_DOWN) { if (!( (re->ticks < cur->ticks) || ( (re->ticks == cur->ticks) && (re->hops <= cur->hops)))) { /* route re-> is worse */ return; } /* route is at least good as * existing one */ if ( (re->ticks==cur->ticks) && (re->hops==cur->hops)) { /* new route has the same cost - reset timer*/ if (!( (ifc_net(src_ifc)==rt_net(cur)) && (!ipx_node_equal(cur->router_node, src_node)))) { cur->timers[ifc_get_index(src_ifc)]=0; } } else { /* new route is better */ LOG_START; fprintf(log_file,"CHANGE "); fprint_route(log_file,cur); LOG_END; /* update table */ cur->ticks=re->ticks; cur->hops=re->hops; cur->router_interface = src_ifc; ipx_assign_node(cur->router_node, src_node); setup_timers(cur,src_ifc); LOG_START; fprintf(log_file,"\nto "); fprint_route(log_file,cur); fprintf(log_file,"\n"); LOG_END; /* update kernel table */ ipx_rip_rt_add(cur); /* send info bcast */ output_broadcast(cur,0); } } else { /* network down response */ if (ifc_net(src_ifc)==rt_net(cur)) { if (ipx_node_equal(cur->router_node, src_node)) { LOG_START; fprintf(log_file,"DELETE "); fprint_route(log_file,cur); fprintf(log_file, " (net down)\n"); LOG_END; /* update kernel table */ ipx_rip_rt_del(cur->network); /* send info bcast */ cur->hops=IPX_RIP_NET_DOWN; output_broadcast(cur,1); /* update table */ delete_route(cur); } else { /* my router alive */ } } else { cur->timers[ifc_get_index(src_ifc)] =EXPIRE_TIME; } } return; } if (re->hops<=IPX_RIP_NET_DOWN) { struct rip_table *rt=add_route(htonl(re->network), re->hops,re->ticks, src_ifc,src_node); if (rt!=NULL) { /* update kernel table */ ipx_rip_rt_add(rt); /* send info bcast */ output_broadcast(rt,0); LOG_START; fprintf(log_file,"ADD "); fprint_route(log_file,rt); fprintf(log_file,"\n"); LOG_END; } } } void handle_rip(struct rip_packet *pkt, int len, struct sockaddr_ipx* sipx, struct ipx_interface *src_ifc) { struct rip_entry *re=pkt->rip_entries; int nent=(len-2)/8; if (ifc_net(src_ifc) != ntohl(sipx->sipx_network)) { LOG_START; fprintf(log_file,"RIP from non-local net on ifc "); ipx_fprint_network(log_file, ifc_net(src_ifc)); fprintf(log_file, "\n"); ipx_fprint_network(log_file,ntohl(sipx->sipx_network)); fprintf(log_file," (ignored)\n"); LOG_END; return; } if (lenifnode,sipx->sipx_node)) && ((unsigned short)sipx->sipx_port==htons(IPX_RIP_PORT))) { DL_START; fprintf(log_file,"My packet (ignored)\n"); DL_END; return; } switch (ntohs(pkt->operation)) { case IPX_RIP_OP_REQUEST: output_set_destination(src_ifc, sipx->sipx_node, ntohs(sipx->sipx_port)); for(;nent--;re++) { handle_rip_request(re, src_ifc); } ipx_rip_output_flush(&(src_ifc->r_output)); output_set_destination(src_ifc, IPX_BROADCAST, IPX_RIP_PORT); break; case IPX_RIP_OP_RESPONSE: /* option: ignore responses from non RIP ports * if (sipx->sipx_port!=htons(IPX_RIP_PORT)) return; */ output_set_destination(NULL, IPX_BROADCAST, IPX_RIP_PORT); for(;nent--;re++) { handle_rip_response(re, src_ifc, sipx->sipx_node); } output_flushall(); break; default: LOG_START; fprintf(log_file,"Unknown RIP operation\n"); LOG_END; break; } } void ipx_rip_do_aging(int rate, int do_broadcast) { struct rip_table *cur; int routes_died = 0; DL_START; fprintf(log_file,"DO RIP AGING\n"); DL_END; output_set_destination(NULL, IPX_BROADCAST, IPX_RIP_PORT); for(cur=rtable; cur!=NULL; cur=cur->next) { int i; ifc_timer* tm; for (i = first_ifc_index(); i >= 0; i = next_ifc_index(i)) { tm = &(cur->timers[i]); if (!is_expired(tm)) { (*tm)+=rate; } } tm=cur->timers+ifc_get_index(cur->router_interface); /* cannot timeout iface !*/ if (cur->network==rt_net(cur)) { *tm=0; } if (is_expired(tm)) { /* net is down */ LOG_START; fprintf(log_file,"DELETE "); fprint_route(log_file,cur); fprintf(log_file," (timed out)\n"); LOG_END; /* update kernel table */ ipx_rip_rt_del(cur->network); /* send info bcast */ cur->hops=IPX_RIP_NET_DOWN; output_broadcast(cur,1); /* table to be updated, delayed */ routes_died = 1; } else { if (do_broadcast) { output_broadcast(cur,0); } } } output_flushall(); /* Delete all bogus routes */ if (routes_died != 0) { delete_invalid_routes(); } } int ipx_rip_init_ifc(struct ipx_interface *ifc, IPXNet network, char *device, int type, void *data) { (void)type; (void)data; struct sockaddr_ipx sipx; if (ipx_rip_output_init(&(ifc->r_output), network) != 0) { LOG_START; fprintf(log_file, "out of memory allocating output buffer\n"); LOG_END; return -1; } if (add_route(network,1,find_ticks(device),ifc,ifc->ifnode) == NULL) { LOG_START; fprintf(log_file, "cannot add route to iface (out of memory)\n"); LOG_END; return -1; } if ((ifc->r_output.sk = socket(AF_IPX, SOCK_DGRAM, PF_IPX)) < 0) { LOG_START; fprintf(log_file, "can't open socket: %s\n", strerror(errno)); LOG_END; return -1; } memset(&sipx, 0, sizeof(sipx)); sipx.sipx_family = AF_IPX; sipx.sipx_network = htonl(network); ipx_assign_node(sipx.sipx_node, IPX_THIS_NODE); sipx.sipx_port = htons(IPX_RIP_PORT); sipx.sipx_type = IPX_RIP_PTYPE; if (bind(ifc->r_output.sk, (struct sockaddr *)&sipx, sizeof(sipx)) < 0) { LOG_START; fprintf(log_file, "can't bind socket: %s\n", strerror(errno)); LOG_END; return -1; } if (ipx_kern_enable_broadcast(ifc->r_output.sk) != 0) { LOG_START; fprintf(log_file, "cant' enable broadcast\n"); LOG_END; return -1; } LOG_START; ipx_fprint_network(log_file, network); fprintf(log_file,":"); ipx_fprint_node(log_file, ifc->ifnode); fprintf(log_file,"\n"); LOG_END; return 0; } static void ipx_rip_deinit_ifc(struct ipx_interface *ifc) { close(ifc->r_output.sk); } static int ipx_rip_del_old_rt(IPXNet net, IPXNet rt_net, IPXNode node, int type, void *data) { (void)rt_net; (void)node; (void)data; if ((type & IPX_KRT_ROUTE) != 0) { /* remove old routes */ ipx_rip_rt_del(net); } return 0; } void ipx_rip_del_old_routes(void) { ipx_kern_scan_rtable(ipx_rip_del_old_rt, NULL); } void ipx_rip_initial_broadcasts(void) { struct rip_table* rt; struct ipx_interface* ifc; ipx_rip_output_func=output_sendto; output_set_destination(NULL, IPX_BROADCAST, IPX_RIP_PORT); for(rt=rtable;rt!=NULL;rt=rt->next) { output_broadcast(rt,0); } output_flushall(); ifc = first_interface(); while (ifc != NULL) { ipx_rip_output_request(&(ifc->r_output), IPX_RIP_GENERAL_RQ); ifc = next_interface(ifc); } output_flushall(); } void ipx_rip_initial_broadcast(struct ipx_interface *ifc) { struct rip_table *cur; output_set_destination(ifc, IPX_BROADCAST, IPX_RIP_PORT); for(cur=rtable; cur!=NULL; cur=cur->next) { if (is_rt_target(cur, ifc)) { ipx_rip_output_response(&(ifc->r_output), cur->network, cur->hops, cur->ticks,0); } if (cur->router_interface == ifc) { output_broadcast(cur, 0); } } ipx_rip_output_request(&(ifc->r_output), IPX_RIP_GENERAL_RQ); output_flushall(); } void ipx_rip_down_ifc(struct ipx_interface *ifc) { struct rip_table *rt; LOG_ENTRY; LOG_START; fprintf(log_file, "RIP DOWN INTERFACE "); ipx_fprint_network(log_file, ifc_net(ifc)); fprintf(log_file, "\n"); LOG_END; for (rt = rtable; rt != NULL; rt = rt->next) { if (rt->router_interface != ifc) { /* rt is not via ifc */ continue; } /* cur is via ifc, so down it */ LOG_START; fprintf(log_file,"DELETE "); fprint_route(log_file,rt); fprintf(log_file, " (ifc down)\n"); LOG_END; /* Update of kernel table unneccessary */ /* send info bcast */ rt->hops=IPX_RIP_NET_DOWN; output_broadcast(rt,1); } output_flushall(); delete_invalid_routes(); ipx_rip_deinit_ifc(ifc); } void ipx_rip_done(void) { struct rip_table* cur; LOG_ENTRY; LOG_START; fprintf(log_file,"RIP Shutdown start\n"); LOG_END; output_set_destination(NULL, IPX_BROADCAST, IPX_RIP_PORT); for(cur=rtable; cur!=NULL; cur=cur->next) { /* update kernel table (don't delete ifaces)*/ if (cur->network != rt_net(cur)) { ipx_rip_rt_del(cur->network); LOG_START fprintf(log_file,"DELETE "); fprint_route(log_file,cur); fprintf(log_file," (shutdown)\n"); LOG_END } /* send info bcast */ cur->hops=IPX_RIP_NET_DOWN; output_broadcast(cur,1); /* update table */ delete_route(cur); } output_flushall(); LOG_ENTRY; LOG_START; fprintf(log_file,"RIP Shutdown end\n"); LOG_END; }