X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=slip.c;h=9e63cb3cd6f3714c37524ca10f1e66f70c6cf9a1;hp=055caf5b6ab293d8fbed819bd5153f33a1f21c71;hb=HEAD;hpb=389070fed67552c613ce5afd3cdb436f8f8c538f diff --git a/slip.c b/slip.c index 055caf5..6631fae 100644 --- a/slip.c +++ b/slip.c @@ -3,10 +3,30 @@ just whole packets. When transmitting we need to bytestuff anyway, and may be part-way through receiving. */ +/* + * This file is part of secnet. + * See README for full list of copyright holders. + * + * secnet 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 3 of the License, or + * (at your option) any later version. + * + * secnet 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 + * version 3 along with secnet; if not, see + * https://www.gnu.org/licenses/gpl.html. + */ + #include "secnet.h" #include "util.h" #include "netlink.h" #include "process.h" +#include "unaligned.h" #include #include #include @@ -23,17 +43,39 @@ struct slip { struct buffer_if *buff; /* We unstuff received packets into here and send them to the netlink code. */ bool_t pending_esc; + bool_t ignoring_packet; /* If this packet was corrupt or overlong, + we ignore everything up to the next END */ netlink_deliver_fn *netlink_to_tunnel; - uint32_t local_address; }; /* Generic SLIP mangling code */ +static void slip_write(int fd, const uint8_t *p, size_t l) +{ + while (l) { + ssize_t written=write(fd,p,l); + if (written<0) { + if (errno==EINTR) { + continue; + } else if (iswouldblock(errno)) { + lg_perror(0,"slip",0,M_ERR,errno,"write() (packet(s) lost)"); + return; + } else { + fatal_perror("slip_stuff: write()"); + } + } + assert(written>0); + assert((size_t)written<=l); + p+=written; + l-=written; + } +} + static void slip_stuff(struct slip *st, struct buffer_if *buf, int fd) { uint8_t txbuf[DEFAULT_BUFSIZE]; uint8_t *i; - uint32_t j=0; + int32_t j=0; BUF_ASSERT_USED(buf); @@ -54,16 +96,12 @@ static void slip_stuff(struct slip *st, struct buffer_if *buf, int fd) break; } if ((j+2)>DEFAULT_BUFSIZE) { - if (write(fd,txbuf,j)<0) { - fatal_perror("slip_stuff: write()"); - } + slip_write(fd,txbuf,j); j=0; } } txbuf[j++]=SLIP_END; - if (write(fd,txbuf,j)<0) { - fatal_perror("slip_stuff: write()"); - } + slip_write(fd,txbuf,j); BUF_FREE(buf); } @@ -73,35 +111,67 @@ static void slip_unstuff(struct slip *st, uint8_t *buf, uint32_t l) BUF_ASSERT_USED(st->buff); for (i=0; ibuff->size) + buffer_init(st->buff,calculate_max_start_pad()); + if (st->pending_esc) { st->pending_esc=False; switch(buf[i]) { case SLIP_ESCEND: - *(uint8_t *)buf_append(st->buff,1)=SLIP_END; + outputchr=SLIP_END; break; case SLIP_ESCESC: - *(uint8_t *)buf_append(st->buff,1)=SLIP_ESC; + outputchr=SLIP_ESC; break; default: - fatal("userv_afterpoll: bad SLIP escape character"); + if (!st->ignoring_packet) { + Message(M_WARNING, "userv_afterpoll: bad SLIP escape" + " character, dropping packet\n"); + } + st->ignoring_packet=True; + outputchr=OUTPUT_NOTHING; + break; } } else { switch (buf[i]) { case SLIP_END: - if (st->buff->size>0) { - st->netlink_to_tunnel(&st->nl,st->buff); - BUF_ALLOC(st->buff,"userv_afterpoll"); - } - buffer_init(st->buff,st->nl.max_start_pad); + outputchr=OUTPUT_END; break; case SLIP_ESC: st->pending_esc=True; + outputchr=OUTPUT_NOTHING; break; default: - *(uint8_t *)buf_append(st->buff,1)=buf[i]; + outputchr=buf[i]; break; } } + + if (st->ignoring_packet) { + if (outputchr == OUTPUT_END) { + st->ignoring_packet=False; + st->buff->size=0; + } + } else { + if (outputchr == OUTPUT_END) { + if (st->buff->size>0) { + st->netlink_to_tunnel(&st->nl,st->buff); + BUF_ALLOC(st->buff,"userv_afterpoll"); + } + st->buff->size=0; + } else if (outputchr != OUTPUT_NOTHING) { + if (buf_remaining_space(st->buff)) { + buf_append_uint8(st->buff,outputchr); + } else { + Message(M_WARNING, "userv_afterpoll: dropping overlong" + " SLIP packet\n"); + st->ignoring_packet=True; + } + } + } } } @@ -112,10 +182,9 @@ static void slip_init(struct slip *st, struct cloc loc, dict_t *dict, netlink_init(&st->nl,st,loc,dict, "netlink-userv-ipif",NULL,to_host); st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"name",loc); - st->local_address=string_item_to_ipaddr( - dict_find_item(dict,"local-address", True, name, loc),"netlink"); BUF_ALLOC(st->buff,"slip_init"); st->pending_esc=False; + st->ignoring_packet=False; } /* Connection to the kernel through userv-ipif */ @@ -132,25 +201,23 @@ struct userv { }; static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, - int *timeout_io, const struct timeval *tv_now, - uint64_t *now) + int *timeout_io) { struct userv *st=sst; if (st->rxfd!=-1) { - *nfds_io=2; + BEFOREPOLL_WANT_FDS(2); fds[0].fd=st->txfd; fds[0].events=0; /* Might want to pick up POLLOUT sometime */ fds[1].fd=st->rxfd; fds[1].events=POLLIN; } else { - *nfds_io=0; + BEFOREPOLL_WANT_FDS(0); } return 0; } -static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds, - const struct timeval *tv_now, uint64_t *now) +static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct userv *st=sst; uint8_t rxbuf[DEFAULT_BUFSIZE]; @@ -164,7 +231,7 @@ static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds, if (fds[1].revents&POLLIN) { l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE); if (l<0) { - if (errno!=EINTR) + if (errno!=EINTR && !iswouldblock(errno)) fatal_perror("%s: userv_afterpoll: read(rxfd)", st->slip.nl.name); } else if (l==0) { @@ -179,6 +246,14 @@ static void userv_deliver_to_kernel(void *sst, struct buffer_if *buf) { struct userv *st=sst; + if (buf->size > st->slip.nl.mtu) { + Message(M_ERR,"%s: packet of size %"PRIu32" exceeds mtu %"PRIu32":" + " cannot be injected into kernel, dropped\n", + st->slip.nl.name, buf->size, st->slip.nl.mtu); + BUF_FREE(buf); + return; + } + slip_stuff(&st->slip,buf,st->txfd); } @@ -191,19 +266,13 @@ static void userv_userv_callback(void *sst, pid_t pid, int status) "(expected %d)\n",pid,st->pid); return; } - if (!st->expecting_userv_exit) { - if (WIFEXITED(status)) { - fatal("%s: userv exited unexpectedly with status %d", - st->slip.nl.name,WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - fatal("%s: userv exited unexpectedly: uncaught signal %d", - st->slip.nl.name,WTERMSIG(status)); - } else { - fatal("%s: userv stopped unexpectedly"); - } + if (!(st->expecting_userv_exit && + (!status || + (WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)))) { + lg_exitstatus(0,st->slip.nl.name,0, + st->expecting_userv_exit ? M_WARNING : M_FATAL, + status,"userv"); } - Message(M_WARNING,"%s: userv subprocess died with status %d\n", - st->slip.nl.name,WEXITSTATUS(status)); st->pid=0; } @@ -222,10 +291,7 @@ static void userv_entry(void *sst) dup2(st->in,0); dup2(st->out,1); - /* XXX close all other fds */ setsid(); - /* XXX We really should strdup() all of argv[] but because we'll just - exit anyway if execvp() fails it doesn't seem worth bothering. */ execvp(st->path,(char *const*)st->argv); perror("userv-entry: execvp()"); exit(1); @@ -233,10 +299,9 @@ static void userv_entry(void *sst) static void userv_invoke_userv(struct userv *st) { - struct userv_entry_rec *er; + struct userv_entry_rec er[1]; int c_stdin[2]; int c_stdout[2]; - string_t addrs; string_t nets; string_t s; struct netlink_client *r; @@ -252,14 +317,14 @@ static void userv_invoke_userv(struct userv *st) /* This is where we actually invoke userv - all the networks we'll be using should already have been registered. */ - addrs=safe_malloc(512,"userv_invoke_userv:addrs"); - snprintf(addrs,512,"%s,%s,%d,slip", - ipaddr_to_string(st->slip.local_address), + char addrs[512]; + snprintf(addrs,sizeof(addrs),"%s,%s,%d,slip", + ipaddr_to_string(st->slip.nl.local_address), ipaddr_to_string(st->slip.nl.secnet_address),st->slip.nl.mtu); allnets=ipset_new(); for (r=st->slip.nl.clients; r; r=r->next) { - if (r->up) { + if (r->link_quality > LINK_QUALITY_UNUSED) { struct ipset *nan; r->kup=True; nan=ipset_union(allnets,r->networks); @@ -275,7 +340,6 @@ static void userv_invoke_userv(struct userv *st) s=subnet_to_string(snets->list[i]); strcat(nets,s); strcat(nets,","); - free(s); } nets[strlen(nets)-1]=0; subnet_list_free(snets); @@ -286,17 +350,11 @@ static void userv_invoke_userv(struct userv *st) st->slip.pending_esc=False; /* Invoke userv */ - if (pipe(c_stdin)!=0) { - fatal_perror("userv_invoke_userv: pipe(c_stdin)"); - } - if (pipe(c_stdout)!=0) { - fatal_perror("userv_invoke_userv: pipe(c_stdout)"); - } + pipe_cloexec(c_stdin); + pipe_cloexec(c_stdout); st->txfd=c_stdin[1]; st->rxfd=c_stdout[0]; - er=safe_malloc(sizeof(*r),"userv_invoke_userv: er"); - er->in=c_stdin[0]; er->out=c_stdout[1]; /* The arguments are: @@ -305,7 +363,8 @@ static void userv_invoke_userv(struct userv *st) service-name local-addr,secnet-addr,mtu,protocol route1,route2,... */ - er->argv=safe_malloc(sizeof(*er->argv)*6,"userv_invoke_userv:argv"); + const char *er_argv[6]; + er->argv=er_argv; er->argv[0]=st->userv_path; er->argv[1]=st->service_user; er->argv[2]=st->service_name; @@ -318,9 +377,6 @@ static void userv_invoke_userv(struct userv *st) er, st, st->slip.nl.name); close(er->in); close(er->out); - free(er->argv); - free(er); - free(addrs); free(nets); Message(M_INFO,"%s: userv-ipif pid is %d\n",st->slip.nl.name,st->pid); /* Read a single character from the pipe to confirm userv-ipif is @@ -344,6 +400,11 @@ static void userv_invoke_userv(struct userv *st) st->slip.nl.name,confirm); } } + setnonblock(st->txfd); + setnonblock(st->rxfd); + + add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->txfd); + add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->rxfd); } static void userv_kill_userv(struct userv *st) @@ -362,7 +423,7 @@ static void userv_phase_hook(void *sst, uint32_t newphase) if (newphase==PHASE_RUN) { userv_invoke_userv(st); /* Register for poll() */ - register_for_poll(st, userv_beforepoll, userv_afterpoll, 2, + register_for_poll(st, userv_beforepoll, userv_afterpoll, st->slip.nl.name); } if (newphase==PHASE_SHUTDOWN) { @@ -377,7 +438,7 @@ static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context, item_t *item; dict_t *dict; - st=safe_malloc(sizeof(*st),"userv_apply"); + NEW(st); /* First parameter must be a dict */ item=list_elem(args,0);