/* * Rudimentary BGP speaker. Never sends UPDATEs anywhere. All it does * is handle connection establishment and decode incoming UPDATEs, * spitting them to stdout. * * Run with the local and peer addresses and the AS on the command * line, as in * * $0 10.1.2.3 10.5.8.14 65341 * * to speak BGP as a member of AS 65341 with 10.5.8.14, using 10.1.2.3 * as the local BGP ID. (The local address is used only to determine * the local BGP ID; it does not constrain what address(es) * communication takes place over.) * * IPv4-only. Given how BGP IDs are defined, this may not be fixable. */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #define MAXMSG 4096 /* "The maximum message size is 4096 octets." */ #define TIME_CONNECTRETRY (5*60) #define TIME_OPENSENTHOLD (4*60) #define MSGTYPE_OPEN 1 #define MSGTYPE_UPDATE 2 #define MSGTYPE_NOTIFICATION 3 #define MSGTYPE_KEEPALIVE 4 #define NOTIF_HDR 1 /* Message Header Error */ #define NOTIF_HDR_NOTSYNC 1 /* Connection Not Synchronized */ #define NOTIF_HDR_LENGTH 2 /* Bad Message Length */ #define NOTIF_HDR_TYPE 3 /* Bad Message Type */ #define NOTIF_OPEN 2 /* OPEN Message Error */ #define NOTIF_OPEN_VERSION 1 /* Unsupported Version Number */ #define NOTIF_OPEN_AS 2 /* Bad Peer AS */ #define NOTIF_OPEN_BGPID 3 /* Bad BGP ID */ #define NOTIF_OPEN_OPTPAR 4 /* Unsupported Optional Parameter */ #define NOTIF_OPEN_AUTH 5 /* Authentication Failure */ #define NOTIF_OPEN_HOLD 6 /* Unacceptable Hold Time */ #define NOTIF_UPDATE 3 /* UPDATE Message Error */ #define NOTIF_UPDATE_MALFATTR 1 /* Malformed Attribute List */ #define NOTIF_UPDATE_UNKWK 2 /* Unrecognized Well-known Attribute */ #define NOTIF_UPDATE_MISSWK 3 /* Missing Well-known Attribute */ #define NOTIF_UPDATE_FLAGS 4 /* Attribute Flags Error */ #define NOTIF_UPDATE_LENGTH 5 /* Attribute Length Error */ #define NOTIF_UPDATE_ORIGIN 6 /* Invalid ORIGIN Attribute */ #define NOTIF_UPDATE_LOOP 7 /* AS Routing Loop */ #define NOTIF_UPDATE_NEXTHOP 8 /* Invalid NEXT_HOP Attribute */ #define NOTIF_UPDATE_OPTIONAL 9 /* Optional Attrbute Error */ #define NOTIF_UPDATE_INVNET 10 /* Invalid Network Field */ #define NOTIF_UPDATE_ASPATH 11 /* Malformed AS_PATH */ #define NOTIF_HOLD 4 /* Hold Timer Expired */ #define NOTIF_FSM 5 /* Finite State Machine Error */ #define NOTIF_CEASE 6 /* Cease */ #define HOLDTIME 60 typedef struct conn CONN; typedef struct timer TIMER; struct conn { CONN *link; int fd; int px; int state; #define CS_IDLE 1 #define CS_CONNECT 2 #define CS_ACTIVE 3 #define CS_OPENSENT 4 #define CS_OPENCONFIRM 5 #define CS_ESTABLISHED 6 TIMER *timer_ConnectRetry; TIMER *timer_Hold; TIMER *timer_KeepAlive; char *peer; int holdtime; unsigned int peerid; unsigned char msgbuf[MAXMSG]; int msgfill; } ; struct timer { TIMER *link; const char *tag; int fd; int px; void (*fire)(void *); void *arg; } ; static struct sockaddr_in peer_addr; static int as; static unsigned int ourid; static FILE *dbgf; static char dbgbuf[65536]; static int dbgptr; static int debug; static int accfd; static CONN *conns; static TIMER *timers; static void dprintf(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void dprintf(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vfprintf(dbgf,fmt,ap); va_end(ap); } static TIMER *start_timer(const char *tag, double secs, void (*fire)(void *), void *arg) { TIMER *t; struct itimerval itv; unsigned int s; int us; s = secs; us = (secs - s) * 1000000; while (us >= 1000000) { s ++; us -= 1000000; } while (us < 0) { s --; us += 1000000; } itv.it_value.tv_sec = s; itv.it_value.tv_usec = us; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; t = malloc(sizeof(TIMER)); t->tag = tag; t->fd = socket(AF_TIMER,SOCK_STREAM,0); if (t->fd < 0) { fprintf(stderr,"%s: timer socket: %s\n",__progname,strerror(errno)); exit(1); } t->px = -1; t->fire = fire; t->arg = arg; dprintf("setting %s timer for %u.%06d\n",tag,s,us); write(t->fd,&itv,sizeof(itv)); t->link = timers; timers = t; return(t); } static void stop_timer(TIMER *t) { if (t) { t->fire = 0; dprintf("stopping %s timer\n",t->tag); } } static void sig_usr1(int sig __attribute__((__unused__))) { debug ++; } static void sig_usr2(int sig __attribute__((__unused__))) { debug = 0; } static void dump_debug(void) { int i; fflush(dbgf); if ((dbgptr == 0) && !dbgbuf[0]) return; if (debug) fprintf(stderr,"\n\n\n"); i = dbgptr; while (! dbgbuf[i]) { i ++; if (i >= sizeof(dbgbuf)) i = 0; } if (i < dbgptr) { write(fileno(stderr),&dbgbuf[i],dbgptr-i); } else { write(fileno(stderr),&dbgbuf[i],sizeof(dbgbuf)-i); write(fileno(stderr),&dbgbuf[0],dbgptr); } } static int dbg_w(void *cookie __attribute__((__unused__)), const char *data, int len) { int rv; if (debug) write(fileno(stderr),data,len); rv = len; if (len > sizeof(dbgbuf)) { data += len - sizeof(dbgbuf); len = sizeof(dbgbuf); } if (dbgptr+len <= sizeof(dbgbuf)) { bcopy(data,&dbgbuf[dbgptr],len); } else { bcopy(data,&dbgbuf[dbgptr],sizeof(dbgbuf)-dbgptr); bcopy(data+(sizeof(dbgbuf)-dbgptr),&dbgbuf[0],len-(sizeof(dbgbuf)-dbgptr)); } dbgptr += len; if (dbgptr >= sizeof(dbgbuf)) dbgptr -= sizeof(dbgbuf); return(rv); } static void setup(void) { struct sockaddr_in sin; conns = 0; timers = 0; accfd = socket(AF_INET,SOCK_STREAM,0); if (accfd < 0) { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(errno)); exit(1); } bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(179); if (bind(accfd,(void *)&sin,sizeof(sin)) < 0) { fprintf(stderr,"%s: bind: %s\n",__progname,strerror(errno)); exit(1); } if (listen(accfd,10) < 0) { fprintf(stderr,"%s: listen: %s\n",__progname,strerror(errno)); exit(1); } dbgf = fwopen(0,dbg_w); bzero(&dbgbuf[0],sizeof(dbgbuf)); dbgptr = 0; signal(SIGUSR1,sig_usr1); signal(SIGUSR2,sig_usr2); } static CONN *newconn(int fd) { CONN *c; c = malloc(sizeof(CONN)); c->fd = fd; c->px = -1; c->state = CS_IDLE; c->msgfill = 0; c->timer_ConnectRetry = 0; c->timer_Hold = 0; c->timer_KeepAlive = 0; c->peer = 0; c->link = conns; conns = c; return(c); } static void send_keepalive(CONN *c) { unsigned char msg[19]; dprintf("sending keepalive to %s\n",c->peer); memset(&msg[0],0xff,16); msg[16] = 0; msg[17] = 19; msg[18] = MSGTYPE_KEEPALIVE; write(c->fd,&msg[0],19); } static void send_notification(CONN *c, int e, int s, int l, const void *b) { unsigned char msg[19+2+l]; dprintf("sending notification to %s: %d %d [%d]\n",c->peer,e,s,l); memset(&msg[0],0xff,16); msg[16] = 0; msg[17] = 21 + l; msg[18] = MSGTYPE_NOTIFICATION; msg[19+0] = e; msg[19+1] = s; if (l > 0) bcopy(b,&msg[19+2],l); write(c->fd,&msg[0],21+l); c->state = CS_IDLE; } static void fire_opensent_hold(void *cv) { dprintf("fire_opensent_hold %s\n",((CONN *)cv)->peer); send_notification(cv,NOTIF_HOLD,0,0,0); } static void send_open(CONN *c) { unsigned char msg[19+10]; dprintf("sending open to %s\n",c->peer); memset(&msg[0],0xff,16); msg[16] = 0; msg[17] = 29; msg[18] = MSGTYPE_OPEN; msg[19+0] = 4; msg[19+1] = as >> 8; msg[19+2] = as & 0xff; msg[19+3] = HOLDTIME >> 8; msg[19+4] = HOLDTIME & 0xff; msg[19+5] = 216; msg[19+6] = 46; msg[19+7] = 5; msg[19+8] = 9; msg[19+9] = 0; write(c->fd,&msg[0],29); c->state = CS_OPENSENT; c->timer_Hold = start_timer("Hold",TIME_OPENSENTHOLD,fire_opensent_hold,c); } static void initiate_connect(CONN *); /* forward */ static void fire_active_retry(void *cv) { dprintf("fire_active_retry %s\n",((CONN *)cv)->peer); initiate_connect(cv); } static void connection_completed(CONN *c) { int err; socklen_t len; len = sizeof(err); if (getsockopt(c->fd,SOL_SOCKET,SO_ERROR,&err,&len) < 0) { fprintf(stderr,"%s: getsockopt SO_ERROR: %s\n",__progname,strerror(errno)); exit(1); } switch (err) { case 0: dprintf("connection_completed %s: success\n",c->peer); stop_timer(c->timer_ConnectRetry); c->timer_ConnectRetry = 0; send_open(c); break; default: dprintf("connection_completed %s: %s\n",c->peer,strerror(err)); close(c->fd); stop_timer(c->timer_ConnectRetry); c->timer_ConnectRetry = start_timer("ConnectRetry",TIME_CONNECTRETRY,fire_active_retry,c); c->state = CS_ACTIVE; break; } } static void fire_connect_retry(void *cv) { CONN *c; dprintf("fire_connect_retry %s\n",((CONN *)cv)->peer); c = cv; close(c->fd); initiate_connect(c); } static void initiate_connect(CONN *c) { int fd; struct sockaddr_in sin; fd = socket(AF_INET,SOCK_STREAM,0); if (fd < 0) { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(errno)); exit(1); } fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); sin = peer_addr; free(c->peer); asprintf(&c->peer,"%p(%d=%s/%d)",(void *)c,fd,inet_ntoa(sin.sin_addr),htons(sin.sin_port)); dprintf("initiate_connect %s\n",c->peer); c->fd = fd; c->state = CS_CONNECT; if (connect(fd,(void *)&sin,sizeof(sin)) >= 0) { connection_completed(c); } else if (errno != EINPROGRESS) { fprintf(stderr,"%s: connect: %s\n",__progname,strerror(errno)); exit(1); } else { c->timer_ConnectRetry = start_timer("ConnectRetry",TIME_CONNECTRETRY,fire_connect_retry,c); } } static void start(void) { CONN *c; c = newconn(-1); initiate_connect(c); } static void start_or_exit(void) { static time_t last = 0; time_t now; now = time(0); if (now > last+3600) { start(); last = now; return; } exit(1); } static void handleargs(int ac, char **av) { struct addrinfo hints; struct addrinfo *ai; int v; char *ep; if (ac != 4) { fprintf(stderr,"Usage: %s our-IP peer-IP AS-num\n",__progname); exit(1); } hints.ai_flags = 0; hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; v = getaddrinfo(av[1],0,&hints,&ai); if (v) { fprintf(stderr,"%s: %s: %s\n",__progname,av[1],gai_strerror(v)); exit(1); } if (ai->ai_next) { fprintf(stderr,"%s: %s: multiple addresses\n",__progname,av[1]); exit(1); } if (ai->ai_family != PF_INET) { fprintf(stderr,"%s: %s: not INET\n",__progname,av[1]); exit(1); } if (ai->ai_addrlen != sizeof(struct sockaddr_in)) { fprintf(stderr,"%s: %s: address size wrong (%d, not %d)\n",__progname,av[1],(int)ai->ai_addrlen,(int)sizeof(struct sockaddr_in)); exit(1); } ourid = ntohl(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr); hints.ai_flags = 0; hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; v = getaddrinfo(av[2],"179",&hints,&ai); if (v) { fprintf(stderr,"%s: %s/179: %s\n",__progname,av[2],gai_strerror(v)); exit(1); } if (ai->ai_next) { fprintf(stderr,"%s: %s/179: multiple addresses\n",__progname,av[2]); exit(1); } if (ai->ai_family != PF_INET) { fprintf(stderr,"%s: %s/179: not INET\n",__progname,av[2]); exit(1); } if (ai->ai_addrlen != sizeof(struct sockaddr_in)) { fprintf(stderr,"%s: %s/179: address size wrong (%d, not %d)\n",__progname,av[2],(int)ai->ai_addrlen,(int)sizeof(struct sockaddr_in)); exit(1); } peer_addr = *(struct sockaddr_in *)ai->ai_addr; freeaddrinfo(ai); as = strtol(av[3],&ep,0); if (*ep || (ep == av[3])) { fprintf(stderr,"%s: %s: not a number\n",__progname,av[3]); exit(1); } } static void do_accept(void) { int new; struct sockaddr_in sin; socklen_t sinlen; int v; CONN *c; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; sinlen = sizeof(sin); new = accept(accfd,(void *)&sin,&sinlen); if (new < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: accept: %s\n",__progname,strerror(errno)); exit(1); } if (sin.sin_family != AF_INET) { fprintf(stderr,"%s: accepted connection isn't INET\n",__progname); close(new); return; } v = getnameinfo((void *)&sin,sizeof(sin),&hn[0],NI_MAXHOST,&pn[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV); if (v) { fprintf(stderr,"%s: getnameinfo: %s\n",__progname,strerror(v)); exit(1); } if (sin.sin_addr.s_addr != peer_addr.sin_addr.s_addr) { fprintf(stderr,"%s: connection from %s/%s, wrong peer\n",__progname,&hn[0],&pn[0]); close(new); return; } dprintf("%s: connection from %s/%s\n",__progname,&hn[0],&pn[0]); c = newconn(new); asprintf(&c->peer,"%p(%d=%s/%s)",(void *)c,new,&hn[0],&pn[0]); send_open(c); } static void fire_openconfirm_hold(void *cv) { dprintf("fire_openconfirm_hold %s\n",((CONN *)cv)->peer); send_notification(cv,NOTIF_HOLD,0,0,0); } static void fire_openconfirm_keepalive(void *cv) { CONN *c; dprintf("fire_openconfirm_keepalive %s\n",((CONN *)cv)->peer); c = cv; send_keepalive(c); c->timer_KeepAlive = start_timer("KeepAlive",c->holdtime/3.0,fire_openconfirm_keepalive,c); } static void conn_msg_open(CONN *c, const unsigned char *data, int len) { CONN *c2; int h; unsigned int id; dprintf("conn_msg_open %s\n",c->peer); if (c->state != CS_OPENSENT) { send_notification(c,NOTIF_FSM,0,0,0); return; } if (len < 10) abort(); if (data[0] != 4) { unsigned char v[2]; /* RFC 1771 is not clear on what value to send if the incoming value is less than the minimum supported locally. */ v[0] = 0; v[1] = 4; send_notification(c,NOTIF_OPEN,NOTIF_OPEN_VERSION,2,&v[0]); return; } h = (data[3] * 256) + data[4]; c->holdtime = (h < HOLDTIME) ? h : HOLDTIME; switch (c->holdtime) { case 1: case 2: send_notification(c,NOTIF_OPEN,NOTIF_OPEN_HOLD,0,0); return; break; } id = (data[5] * 0x01000000) + (data[6] * 0x00010000) + (data[7] * 0x00000100) + data[8]; for (c2=conns;c2;c2=c2->link) { switch (c2->state) { case CS_OPENCONFIRM: if (c->peerid == id) { if (ourid < id) { send_notification(c2,NOTIF_CEASE,0,0,0); } else { send_notification(c,NOTIF_CEASE,0,0,0); return; } } break; case CS_ESTABLISHED: if (c->peerid == id) { send_notification(c,NOTIF_CEASE,0,0,0); return; } break; } } send_keepalive(c); stop_timer(c->timer_Hold); stop_timer(c->timer_KeepAlive); if (c->holdtime) { c->timer_Hold = start_timer("Hold",c->holdtime,fire_openconfirm_hold,c); c->timer_KeepAlive = start_timer("KeepAlive",c->holdtime/3.0,fire_openconfirm_keepalive,c); } else { int on; on = 1; setsockopt(c->fd,SOL_SOCKET,SO_KEEPALIVE,&on,sizeof(on)); c->timer_Hold = 0; c->timer_KeepAlive = 0; } c->state = CS_OPENCONFIRM; } static void fire_established_hold(void *cv) { dprintf("fire_established_hold %s\n",((CONN *)cv)->peer); send_notification(cv,NOTIF_HOLD,0,0,0); } static void conn_msg_update(CONN *c, const unsigned char *data, int len) { int i; struct timeval now_tv; time_t now_tt; struct tm *now_tm; dprintf("conn_msg_update %s\n",c->peer); if (c->state != CS_ESTABLISHED) { send_notification(c,NOTIF_FSM,0,0,0); return; } if (c->holdtime) { stop_timer(c->timer_Hold); c->timer_Hold = start_timer("Hold",c->holdtime,fire_established_hold,c); } gettimeofday(&now_tv,0); now_tt = now_tv.tv_sec; now_tm = gmtime(&now_tt); printf("%04d-%02d-%02d %02d:%02d:%02d.%06lu ", now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, (unsigned long int)now_tv.tv_usec ); for (i=0;ipeer); if (len < 2) { dump_debug(); fprintf(stderr,"%s: NOTIFICATION received with length %d:",__progname,len); for (i=0;istate = CS_IDLE; return; } pf = 0; dump_debug(); fprintf(stderr,"%s: NOTIFICATION received\n",__progname); switch (data[0]) { case NOTIF_HDR: fprintf(stderr,"%s: Message Header Error: ",__progname); switch (data[1]) { case NOTIF_HDR_NOTSYNC: fprintf(stderr,"Connection Not Synchronized"); xl = 2; break; case NOTIF_HDR_LENGTH: fprintf(stderr,"Bad Message Length"); xl = 4; pf = print_2_d; break; case NOTIF_HDR_TYPE: fprintf(stderr,"Bad Message Type"); xl = 3; pf = print_1_d; break; default: fprintf(stderr,"subcode %d",data[1]); xl = -1; break; } break; case NOTIF_OPEN: fprintf(stderr,"%s: OPEN Message Error: ",__progname); switch (data[1]) { case NOTIF_OPEN_VERSION: fprintf(stderr,"Unsupported Version Number"); xl = 4; pf = print_2_d; break; case NOTIF_OPEN_AS: fprintf(stderr,"Bad Peer AS"); xl = 2; break; case NOTIF_OPEN_BGPID: fprintf(stderr,"Bad BGP Identifier"); xl = 2; break; case NOTIF_OPEN_OPTPAR: fprintf(stderr,"Unsupported Optional Parameter"); xl = 2; break; case NOTIF_OPEN_AUTH: fprintf(stderr,"Authentication Failure"); xl = 2; break; case NOTIF_OPEN_HOLD: fprintf(stderr,"Unacceptable Hold Time"); xl = 2; break; default: fprintf(stderr,"subcode %d",data[1]); xl = -1; break; } break; case NOTIF_UPDATE: fprintf(stderr,"%s: UPDATE Message Error: ",__progname); switch (data[1]) { case NOTIF_UPDATE_MALFATTR: fprintf(stderr,"Malformed Attribute List"); xl = 2; break; case NOTIF_UPDATE_UNKWK: fprintf(stderr,"Unrecognized Well-known Attribute"); xl = 2; /* XXX contains unrecognized attribute */ break; case NOTIF_UPDATE_MISSWK: fprintf(stderr,"Missing Well-known Attribute"); xl = 2; /* XXX contains missing attribute's code */ break; case NOTIF_UPDATE_FLAGS: fprintf(stderr,"Attribute Flags Error"); xl = 2; /* XXX contains erroneous attribute */ break; case NOTIF_UPDATE_LENGTH: fprintf(stderr,"Attribute Length Error"); xl = 2; /* XXX contains erroneous attribute */ break; case NOTIF_UPDATE_ORIGIN: fprintf(stderr,"Invalid ORIGIN Attribute"); xl = 2; /* XXX contains unrecognized attribute */ break; case NOTIF_UPDATE_LOOP: fprintf(stderr,"AS Routing Loop"); xl = 2; break; case NOTIF_UPDATE_NEXTHOP: fprintf(stderr,"Invalid NEXT_HOP Attribute"); xl = 2; /* XXX contains erroneous attribute */ break; case NOTIF_UPDATE_OPTIONAL: fprintf(stderr,"Optional Attrbute Error"); xl = 2; /* XXX contains erroneous attribute */ break; case NOTIF_UPDATE_INVNET: fprintf(stderr,"Invalid Network Field"); xl = 2; break; case NOTIF_UPDATE_ASPATH: fprintf(stderr,"Malformed AS_PATH"); xl = 2; break; default: fprintf(stderr,"subcode %d",data[1]); xl = -1; break; } break; case NOTIF_HOLD: fprintf(stderr,"%s: Hold Timer Expired",__progname); switch (data[1]) { case 0: xl = 2; pf = colon; break; default: fprintf(stderr,": subcode %d",data[1]); xl = -1; break; } break; case NOTIF_FSM: fprintf(stderr,"%s: Finite State Machine Error",__progname); switch (data[1]) { case 0: xl = 2; pf = colon; break; default: fprintf(stderr,": subcode %d",data[1]); xl = -1; break; } break; case NOTIF_CEASE: fprintf(stderr,"%s: Cease: subcode %d",__progname,data[1]); xl = -1; break; } if (pf) { if (len >= xl) { (*pf)(); } else { fprintf(stderr,"(data too short)"); } } if (len != xl) { fprintf(stderr,"\n%s: data:",__progname); for (i=0;ipeer); c = cv; send_keepalive(c); c->timer_KeepAlive = start_timer("KeepAlive",c->holdtime/3.0,fire_established_keepalive,c); } static void conn_msg_keepalive(CONN *c) { dprintf("conn_msg_keepalive %s\n",c->peer); switch (c->state) { case CS_OPENCONFIRM: stop_timer(c->timer_Hold); c->timer_Hold = c->holdtime ? start_timer("Hold",c->holdtime,fire_established_hold,c) : 0; stop_timer(c->timer_KeepAlive); c->timer_KeepAlive = c->holdtime ? start_timer("KeepAlive",c->holdtime/3.0,fire_established_keepalive,c) : 0; c->state = CS_ESTABLISHED; break; case CS_ESTABLISHED: stop_timer(c->timer_Hold); c->timer_Hold = c->holdtime ? start_timer("Hold",c->holdtime,fire_established_hold,c) : 0; break; default: send_notification(c,NOTIF_FSM,0,0,0); break; } } static void conn_msg(CONN *c) { __label__ retnow; int i; int l; static void fail(int e, int s, int l, const void *b) { send_notification(c,e,s,l,b); c->state = CS_IDLE; goto retnow; } if (0) { retnow:; return; } if (c->msgfill < 19) abort(); dprintf("conn_msg %s:",c->peer); for (i=0;imsgfill;i++) dprintf(" %02x",c->msgbuf[i]); dprintf("\n"); for (i=0;i<16;i++) { if (c->msgbuf[i] != 0xff) fail(NOTIF_HDR,NOTIF_HDR_NOTSYNC,0,0); } l = (c->msgbuf[16] * 256) + c->msgbuf[17]; if ((l < 19) || (l > 4096)) fail(NOTIF_HDR,NOTIF_HDR_LENGTH,2,&c->msgbuf[16]); switch (c->msgbuf[18]) { case MSGTYPE_OPEN: if (l < 19+10) fail(NOTIF_HDR,NOTIF_HDR_LENGTH,2,&c->msgbuf[16]); conn_msg_open(c,&c->msgbuf[19],l-19); break; case MSGTYPE_UPDATE: if (l < 19+4) fail(NOTIF_HDR,NOTIF_HDR_LENGTH,2,&c->msgbuf[16]); conn_msg_update(c,&c->msgbuf[19],l-19); break; case MSGTYPE_NOTIFICATION: if (l < 19+2) fail(NOTIF_HDR,NOTIF_HDR_LENGTH,2,&c->msgbuf[16]); conn_msg_notification(c,&c->msgbuf[19],l-19); break; case MSGTYPE_KEEPALIVE: if (l != 19) fail(NOTIF_HDR,NOTIF_HDR_LENGTH,2,&c->msgbuf[16]); conn_msg_keepalive(c); break; default: fail(NOTIF_HDR,NOTIF_HDR_TYPE,1,&c->msgbuf[18]); break; } c->msgfill = 0; } static void conn_read(CONN *c) { int n; int r; switch (c->state) { case CS_CONNECT: connection_completed(c); return; } n = (c->msgfill < 19) ? 19 : ((c->msgbuf[16] * 256) + c->msgbuf[17]); if ((n < 19) || (n > 4096)) { fprintf(stderr,"%s: bad length %d in message\n",__progname,n); abort(); } n -= c->msgfill; if (n < 0) abort(); r = read(c->fd,&c->msgbuf[c->msgfill],n); if (r < 0) { r = errno; dprintf("conn_read %s: %s\n",c->peer,strerror(r)); switch (r) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: network read: %s\n",__progname,strerror(r)); exit(1); } if (r == 0) { dprintf("conn_read %s: EOF\n",c->peer); switch (c->state) { case CS_OPENSENT: close(c->fd); stop_timer(c->timer_ConnectRetry); stop_timer(c->timer_Hold); c->timer_ConnectRetry = start_timer("ConnectRetry",TIME_CONNECTRETRY,fire_active_retry,c); c->timer_Hold = 0; c->state = CS_ACTIVE; break; case CS_OPENCONFIRM: case CS_ESTABLISHED: fprintf(stderr,"%s: network EOF\n",__progname); c->state = CS_IDLE; break; } return; } dprintf("conn_read %s: %d -> %d\n",c->peer,n,r); c->msgfill += r; if (c->msgfill >= 19) { n = ((c->msgbuf[16] * 256) + c->msgbuf[17]); if ((n < 19) || (n > 4096)) { fprintf(stderr,"%s: bad length %d in message\n",__progname,n); abort(); } if (c->msgfill >= n) conn_msg(c); } } static void step(void) { static struct pollfd *pfds = 0; static int apfds = 0; int npfds; CONN *c; CONN **cp; TIMER *t; TIMER **tp; int accpx; int rv; static int addfd(int fd, int ev) { if (npfds >= apfds) pfds = realloc(pfds,(apfds=npfds+8)*sizeof(*pfds)); pfds[npfds].fd = fd; pfds[npfds].events = ev; return(npfds++); } npfds = 0; accpx = addfd(accfd,POLLIN|POLLRDNORM); cp = &conns; while ((c = *cp)) { switch (c->state) { case CS_IDLE: stop_timer(c->timer_ConnectRetry); stop_timer(c->timer_Hold); stop_timer(c->timer_KeepAlive); close(c->fd); *cp = c->link; free(c->peer); free(c); continue; break; case CS_CONNECT: c->px = addfd(c->fd,POLLOUT|POLLWRNORM); break; case CS_ACTIVE: c->px = -1; break; case CS_OPENSENT: case CS_OPENCONFIRM: case CS_ESTABLISHED: c->px = addfd(c->fd,POLLIN|POLLRDNORM); break; default: abort(); break; } cp = &c->link; } if (conns == 0) { start_or_exit(); return; } tp = &timers; while ((t = *tp)) { if (t->fire == 0) { close(t->fd); *tp = t->link; free(t); } else { t->px = addfd(t->fd,POLLIN|POLLRDNORM); tp = &t->link; } } { int i; dprintf("poll:"); for (i=0;ilink) if ((c->px >= 0) && pfds[c->px].revents) conn_read(c); tp = &timers; while ((t = *tp)) { if (t->fire == 0) { close(t->fd); *tp = t->link; free(t); } else if ((t->px >= 0) && pfds[t->px].revents) { *tp = t->link; close(t->fd); dprintf("fire %s: %p(%p)\n",t->tag,(void *)t->fire,t->arg); (*t->fire)(t->arg); free(t); } else { tp = &t->link; } } } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); setup(); while (1) step(); }