ping.

On 2/15/20 12:33 PM, Martijn van Duren wrote:
> This diff does the following:
> - Push the network handling to their own files.
>   This helps with readability and allows for easier addition of other
>   transport layers if we ever choose to (unix socket, ssh, dtls, ...)
> - Allow multiple tcp packets to be handled at the same time.
>   This will helps throughput with multiple backends (agentx).
> - Remove sm_data from the snmp_message. This shaves of ~65k per struct.
>   Right now this isn't that interesting, because we mostly handle 1
>   packet at a time, but as soon as we're leveraging agentx more it
>   is not unlikely that there are multiple packets in flight inside
>   snmpd.
> 
> There's only one intentional function change and that is that the
> outpkts is incremented once the data is pushed to the output buffer
> in tcp.c, instead of when the packet has been send to the kernel.
> I think this is reasonable, since the endeavour is to make the transport
> layer agnostic about how snmp works.
> 
> 
> There's more that I'd like to change, but this diff is large enough as
> is.
> 
> OK?
> 
> martijn@
> 
> Index: Makefile
> ===================================================================
> RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
> retrieving revision 1.16
> diff -u -p -r1.16 Makefile
> --- Makefile  11 May 2019 17:46:02 -0000      1.16
> +++ Makefile  15 Feb 2020 11:25:41 -0000
> @@ -3,6 +3,7 @@
>  PROG=                snmpd
>  MAN=         snmpd.8 snmpd.conf.5
>  SRCS=                parse.y log.c control.c snmpe.c \
> +                 tcp.c udp.c \
>                   mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
>                   pf.c proc.c usm.c agentx.c traphandler.c util.c
>  
> Index: snmpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
> retrieving revision 1.86
> diff -u -p -r1.86 snmpd.h
> --- snmpd.h   2 Jan 2020 10:55:53 -0000       1.86
> +++ snmpd.h   15 Feb 2020 11:25:41 -0000
> @@ -395,23 +395,21 @@ struct pfr_buffer {
>   * daemon structures
>   */
>  
> +struct snmp_conn {
> +     char                     sc_host[HOST_NAME_MAX+1];
> +     void                    *sc_context;
> +     TAILQ_HEAD(, snmp_message) sc_messages;
> +};
> +
>  #define MSG_HAS_AUTH(m)              (((m)->sm_flags & SNMP_MSGFLAG_AUTH) != 
> 0)
>  #define MSG_HAS_PRIV(m)              (((m)->sm_flags & SNMP_MSGFLAG_PRIV) != 
> 0)
>  #define MSG_SECLEVEL(m)              ((m)->sm_flags & SNMP_MSGFLAG_SECMASK)
>  #define MSG_REPORT(m)                (((m)->sm_flags & SNMP_MSGFLAG_REPORT) 
> != 0)
>  
>  struct snmp_message {
> -     int                      sm_sock;
> -     struct sockaddr_storage  sm_ss;
> -     socklen_t                sm_slen;
> -     int                      sm_sock_tcp;
> -     struct event             sm_sockev;
> -     char                     sm_host[HOST_NAME_MAX+1];
> -
> -     struct sockaddr_storage  sm_local_ss;
> -     socklen_t                sm_local_slen;
> +     struct snmp_conn        *sm_conn;
> +     void                    (*sm_reply)(struct snmp_conn *, void *, size_t);
>  
> -     struct ber               sm_ber;
>       struct ber_element      *sm_req;
>       struct ber_element      *sm_resp;
>  
> @@ -423,7 +421,7 @@ struct snmp_message {
>       struct ber_element      *sm_last;
>       struct ber_element      *sm_end;
>  
> -     u_int8_t                 sm_data[READ_BUF_SIZE];
> +     u_int8_t                *sm_data;
>       size_t                   sm_datalen;
>  
>       u_int                    sm_version;
> @@ -464,6 +462,7 @@ struct snmp_message {
>  
>       struct ber_element      *sm_varbind;
>       struct ber_element      *sm_varbindresp;
> +     TAILQ_ENTRY(snmp_message) sm_entry;
>  };
>  
>  /* Defined in SNMPv2-MIB.txt (RFC 3418) */
> @@ -667,10 +666,19 @@ struct kroute   *kroute_getaddr(in_addr_t,
>  struct kif_arp       *karp_first(u_short);
>  struct kif_arp       *karp_getaddr(struct sockaddr *, u_short, int);
>  
> +/* tcp.c */
> +void tcp_accept(int, short, void *);
> +
> +/* udp.c */
> +void udp_recvmsg(int, short, void *);
> +
>  /* snmpe.c */
>  void          snmpe(struct privsep *, struct privsep_proc *);
>  void          snmpe_shutdown(void);
>  void          snmpe_dispatchmsg(struct snmp_message *);
> +ssize_t               snmpe_extract(void *, size_t,
> +                 void (*)(struct snmp_conn *, void *, size_t),
> +                 struct snmp_conn *);
>  
>  /* trap.c */
>  void          trap_init(void);
> Index: snmpe.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
> retrieving revision 1.61
> diff -u -p -r1.61 snmpe.c
> --- snmpe.c   14 Feb 2020 15:08:46 -0000      1.61
> +++ snmpe.c   15 Feb 2020 11:25:41 -0000
> @@ -42,22 +42,15 @@
>  
>  void  snmpe_init(struct privsep *, struct privsep_proc *, void *);
>  int   snmpe_parse(struct snmp_message *);
> -void  snmpe_tryparse(int, struct snmp_message *);
>  int   snmpe_parsevarbinds(struct snmp_message *);
>  void  snmpe_response(struct snmp_message *);
>  void  snmpe_sig_handler(int sig, short, void *);
>  int   snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
>  int   snmpe_bind(struct address *);
> -void  snmpe_recvmsg(int fd, short, void *);
> -void  snmpe_readcb(int fd, short, void *);
> -void  snmpe_writecb(int fd, short, void *);
> -void  snmpe_acceptcb(int fd, short, void *);
> -void  snmpe_prepare_read(struct snmp_message *, int);
>  int   snmpe_encode(struct snmp_message *);
>  void  snmp_msgfree(struct snmp_message *);
>  
>  struct imsgev        *iev_parent;
> -static const struct timeval  snmpe_tcp_timeout = { 10, 0 }; /* 10s */
>  
>  static struct privsep_proc procs[] = {
>       { "parent",     PROC_PARENT,    snmpe_dispatch_parent }
> @@ -111,11 +104,11 @@ snmpe_init(struct privsep *ps, struct pr
>               if (so->s_ipproto == IPPROTO_TCP) {
>                       if (listen(so->s_fd, 5) < 0)
>                               fatalx("snmpe: failed to listen on socket");
> -                     event_set(&so->s_ev, so->s_fd, EV_READ, snmpe_acceptcb, 
> so);
> -                     evtimer_set(&so->s_evt, snmpe_acceptcb, so);
> +                     event_set(&so->s_ev, so->s_fd, EV_READ, tcp_accept, so);
> +                     evtimer_set(&so->s_evt, tcp_accept, so);
>               } else {
>                       event_set(&so->s_ev, so->s_fd, EV_READ|EV_PERSIST,
> -                         snmpe_recvmsg, env);
> +                         udp_recvmsg, env);
>               }
>               event_add(&so->s_ev, NULL);
>       }
> @@ -224,7 +217,6 @@ snmpe_parse(struct snmp_message *msg)
>       char                    *comn;
>       char                    *flagstr, *ctxname;
>       size_t                   len;
> -     struct sockaddr_storage *ss = &msg->sm_ss;
>       struct ber_element      *root = msg->sm_req;
>  
>       msg->sm_errstr = "invalid message";
> @@ -371,17 +363,17 @@ snmpe_parse(struct snmp_message *msg)
>       msg->sm_error = errval;
>       msg->sm_errorindex = erridx;
>  
> -     print_host(ss, msg->sm_host, sizeof(msg->sm_host));
>       if (msg->sm_version == SNMP_V3)
>               log_debug("%s: %s: SNMPv3 context %d, flags %#x, "
>                   "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
> -                 "request %lld", __func__, msg->sm_host, msg->sm_context,
> -                 msg->sm_flags, msg->sm_secmodel, msg->sm_username,
> +                 "request %lld", __func__, msg->sm_conn->sc_host,
> +                 msg->sm_context, msg->sm_flags, msg->sm_secmodel,
> +                 msg->sm_username,
>                   tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
>                   msg->sm_ctxname, msg->sm_request);
>       else
>               log_debug("%s: %s: SNMPv%d '%s' context %d request %lld",
> -                 __func__, msg->sm_host, msg->sm_version + 1,
> +                 __func__, msg->sm_conn->sc_host, msg->sm_version + 1,
>                   msg->sm_community, msg->sm_context, msg->sm_request);
>  
>       return (0);
> @@ -389,8 +381,7 @@ snmpe_parse(struct snmp_message *msg)
>   parsefail:
>       stats->snmp_inasnparseerrs++;
>   fail:
> -     print_host(ss, msg->sm_host, sizeof(msg->sm_host));
> -     log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr);
> +     log_debug("%s: %s: %s", __func__, msg->sm_conn->sc_host, 
> msg->sm_errstr);
>       return (-1);
>  }
>  
> @@ -434,7 +425,7 @@ snmpe_parsevarbinds(struct snmp_message 
>                               else
>                                       stats->snmp_intotalreqvars++;
>                               log_debug("%s: %s: oid %s",
> -                                 __func__, msg->sm_host,
> +                                 __func__, msg->sm_conn->sc_host,
>                                   smi_oid2string(&o, buf, sizeof(buf), 0));
>                               break;
>                       case 1:
> @@ -511,236 +502,65 @@ snmpe_parsevarbinds(struct snmp_message 
>       return (ret);
>   varfail:
>       log_debug("%s: %s: %s, error index %d", __func__,
> -         msg->sm_host, msg->sm_errstr, msg->sm_i);
> +         msg->sm_conn->sc_host, msg->sm_errstr, msg->sm_i);
>       if (msg->sm_error == 0)
>               msg->sm_error = SNMP_ERROR_GENERR;
>       msg->sm_errorindex = msg->sm_i;
>       return (-1);
>  }
>  
> -void
> -snmpe_acceptcb(int fd, short type, void *arg)
> -{
> -     struct listen_sock      *so = arg;
> -     struct sockaddr_storage ss;
> -     socklen_t len = sizeof(ss);
> -     struct snmp_message     *msg;
> -     int afd;
> -
> -     event_add(&so->s_ev, NULL);
> -     if ((type & EV_TIMEOUT))
> -             return;
> -
> -     if ((afd = accept4(fd, (struct sockaddr *)&ss, &len,
> -         SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) {
> -             /* Pause accept if we are out of file descriptors  */
> -             if (errno == ENFILE || errno == EMFILE) {
> -                     struct timeval evtpause = { 1, 0 };
> -
> -                     event_del(&so->s_ev);
> -                     evtimer_add(&so->s_evt, &evtpause);
> -             } else if (errno != EAGAIN && errno != EINTR)
> -                     log_debug("%s: accept4", __func__);
> -             return;
> -     }
> -     if ((msg = calloc(1, sizeof(*msg))) == NULL)
> -             goto fail;
> -
> -     snmpe_prepare_read(msg, afd);
> -     return;
> -fail:
> -     free(msg);
> -     close(afd);
> -     return;
> -}
> -
> -void
> -snmpe_prepare_read(struct snmp_message *msg, int fd)
> -{
> -     msg->sm_sock = fd;
> -     msg->sm_sock_tcp = 1;
> -     event_del(&msg->sm_sockev);
> -     event_set(&msg->sm_sockev, fd, EV_READ,
> -         snmpe_readcb, msg);
> -     event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
> -}
> -
> -void
> -snmpe_tryparse(int fd, struct snmp_message *msg)
> +ssize_t
> +snmpe_extract(void *buf, size_t buflen,
> +    void (*reply)(struct snmp_conn *, void *, size_t), struct snmp_conn 
> *conn)
>  {
>       struct snmp_stats       *stats = &snmpd_env->sc_stats;
> +     struct ber               ber;
> +     struct ber_element      *req;
> +     struct snmp_message     *msg;
> +     ssize_t                  nread;
>  
> -     ober_set_application(&msg->sm_ber, smi_application);
> -     ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
> -     msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
> -     if (msg->sm_req == NULL) {
> +     bzero(&ber, sizeof(ber));
> +     ober_set_application(&ber, smi_application);
> +     ober_set_readbuf(&ber, buf, buflen);
> +     if ((req = ober_read_elements(&ber, NULL)) == NULL) {
>               if (errno == ECANCELED) {
> -                     /* short read; try again */
> -                     snmpe_prepare_read(msg, fd);
> -                     return;
> +                     /* short read */
> +                     return 0;
>               }
> -             goto fail;
> +             return -1;
>       }
> +     nread = ber.br_rptr - (u_char *)buf;
> +
> +     if ((msg = calloc(1, sizeof(*msg))) == NULL) {
> +             log_warn("malloc");
> +             return -1;
> +     }
> +     msg->sm_req = req;
> +     msg->sm_conn = conn;
> +     msg->sm_reply = reply;
> +     msg->sm_data = buf;
> +     msg->sm_datalen = buflen;
> +     TAILQ_INSERT_TAIL(&(conn->sc_messages), msg, sm_entry);
>  
>       if (snmpe_parse(msg) == -1) {
> +             msg->sm_data = NULL;
> +             msg->sm_datalen = 0;
>               if (msg->sm_usmerr && MSG_REPORT(msg)) {
>                       usm_make_report(msg);
>                       snmpe_response(msg);
> -                     return;
> +                     return nread;
>               } else
>                       goto fail;
>       }
> +     msg->sm_data = NULL;
> +     msg->sm_datalen = 0;
>       stats->snmp_inpkts++;
>  
>       snmpe_dispatchmsg(msg);
> -     return;
> - fail:
> -     snmp_msgfree(msg);
> -     close(fd);
> -}
> -
> -void
> -snmpe_readcb(int fd, short type, void *arg)
> -{
> -     struct snmp_message *msg = arg;
> -     ssize_t len;
> -
> -     if (type == EV_TIMEOUT || msg->sm_datalen >= sizeof(msg->sm_data))
> -             goto fail;
> -
> -     len = read(fd, msg->sm_data + msg->sm_datalen,
> -         sizeof(msg->sm_data) - msg->sm_datalen);
> -     if (len <= 0) {
> -             if (errno != EAGAIN && errno != EINTR)
> -                     goto fail;
> -             snmpe_prepare_read(msg, fd);
> -             return;
> -     }
> -
> -     msg->sm_datalen += (size_t)len;
> -     snmpe_tryparse(fd, msg);
> -     return;
> -
> - fail:
> -     snmp_msgfree(msg);
> -     close(fd);
> -}
> -
> -void
> -snmpe_writecb(int fd, short type, void *arg)
> -{
> -     struct snmp_stats       *stats = &snmpd_env->sc_stats;
> -     struct snmp_message     *msg = arg;
> -     struct snmp_message     *nmsg;
> -     ssize_t                  len;
> -     size_t                   reqlen;
> -     struct ber              *ber = &msg->sm_ber;
> -
> -     if (type == EV_TIMEOUT)
> -             goto fail;
> -
> -     len = ber->br_wend - ber->br_wbuf;
> -     ber->br_wptr = ber->br_wbuf;
> -
> -     log_debug("%s: write fd %d len %zd", __func__, fd, len);
> -
> -     len = write(fd, ber->br_wptr, len);
> -     if (len == -1) {
> -             if (errno == EAGAIN || errno == EINTR)
> -                     return;
> -             else
> -                     goto fail;
> -     }
> -
> -     ber->br_wptr += len;
> -
> -     if (ber->br_wptr < ber->br_wend) {
> -             event_del(&msg->sm_sockev);
> -             event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
> -                 snmpe_writecb, msg);
> -             event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
> -             return;
> -     }
> -
> -     stats->snmp_outpkts++;
> -
> -     if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL)
> -             goto fail;
> -
> -     /*
> -      * Reuse the connection.
> -      * In case we already read data of the next message, copy it over.
> -      */
> -     reqlen = ober_calc_len(msg->sm_req);
> -     if (msg->sm_datalen > reqlen) {
> -             memcpy(nmsg->sm_data, msg->sm_data + reqlen,
> -                 msg->sm_datalen - reqlen);
> -             nmsg->sm_datalen = msg->sm_datalen - reqlen;
> -             snmp_msgfree(msg);
> -             snmpe_prepare_read(nmsg, fd);
> -             snmpe_tryparse(fd, nmsg);
> -     } else {
> -             snmp_msgfree(msg);
> -             snmpe_prepare_read(nmsg, fd);
> -     }
> -     return;
> -
> +     return nread;
>   fail:
> -     close(fd);
>       snmp_msgfree(msg);
> -}
> -
> -void
> -snmpe_recvmsg(int fd, short sig, void *arg)
> -{
> -     struct snmpd            *env = arg;
> -     struct snmp_stats       *stats = &env->sc_stats;
> -     ssize_t                  len;
> -     struct snmp_message     *msg;
> -
> -     if ((msg = calloc(1, sizeof(*msg))) == NULL)
> -             return;
> -
> -     msg->sm_sock = fd;
> -     msg->sm_slen = sizeof(msg->sm_ss);
> -     if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
> -         (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
> -         (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
> -             free(msg);
> -             return;
> -     }
> -
> -     stats->snmp_inpkts++;
> -     msg->sm_datalen = (size_t)len;
> -
> -     bzero(&msg->sm_ber, sizeof(msg->sm_ber));
> -     ober_set_application(&msg->sm_ber, smi_application);
> -     ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
> -
> -     msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
> -     if (msg->sm_req == NULL) {
> -             stats->snmp_inasnparseerrs++;
> -             snmp_msgfree(msg);
> -             return;
> -     }
> -
> -#ifdef DEBUG
> -     fprintf(stderr, "recv msg:\n");
> -     smi_debug_elements(msg->sm_req);
> -#endif
> -
> -     if (snmpe_parse(msg) == -1) {
> -             if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
> -                     usm_make_report(msg);
> -                     snmpe_response(msg);
> -                     return;
> -             } else {
> -                     snmp_msgfree(msg);
> -                     return;
> -             }
> -     }
> -
> -     snmpe_dispatchmsg(msg);
> +     return -1;
>  }
>  
>  void
> @@ -761,6 +581,7 @@ snmpe_response(struct snmp_message *msg)
>       struct snmp_stats       *stats = &snmpd_env->sc_stats;
>       u_int8_t                *ptr = NULL;
>       ssize_t                  len;
> +     struct ber               ber;
>  
>       if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL)
>               msg->sm_varbindresp = ober_unlink_elements(msg->sm_pduend);
> @@ -790,25 +611,16 @@ snmpe_response(struct snmp_message *msg)
>       if (snmpe_encode(msg) < 0)
>               goto done;
>  
> -     len = ober_write_elements(&msg->sm_ber, msg->sm_resp);
> -     if (ober_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1)
> +     bzero(&ber, sizeof(ber));
> +     len = ober_write_elements(&ber, msg->sm_resp);
> +     if (ober_get_writebuf(&ber, (void *)&ptr) == -1)
>               goto done;
>  
>       usm_finalize_digest(msg, ptr, len);
> -     if (msg->sm_sock_tcp) {
> -             event_del(&msg->sm_sockev);
> -             event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
> -                 snmpe_writecb, msg);
> -             event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
> -             return;
> -     } else {
> -             len = sendtofrom(msg->sm_sock, ptr, len, 0,
> -                 (struct sockaddr *)&msg->sm_ss, msg->sm_slen,
> -                 (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen);
> -             if (len != -1)
> -                     stats->snmp_outpkts++;
> +     if (msg->sm_conn != NULL) {
> +             msg->sm_reply(msg->sm_conn, ptr, len);
> +             stats->snmp_outpkts++;
>       }
> -
>   done:
>       snmp_msgfree(msg);
>  }
> @@ -816,12 +628,12 @@ snmpe_response(struct snmp_message *msg)
>  void
>  snmp_msgfree(struct snmp_message *msg)
>  {
> -     event_del(&msg->sm_sockev);
> -     ober_free(&msg->sm_ber);
>       if (msg->sm_req != NULL)
>               ober_free_elements(msg->sm_req);
>       if (msg->sm_resp != NULL)
>               ober_free_elements(msg->sm_resp);
> +     if (msg->sm_conn != NULL)
> +             TAILQ_REMOVE(&(msg->sm_conn->sc_messages), msg, sm_entry);
>       free(msg);
>  }
>  
> Index: tcp.c
> ===================================================================
> RCS file: tcp.c
> diff -N tcp.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ tcp.c     15 Feb 2020 11:25:41 -0000
> @@ -0,0 +1,214 @@
> +/*   $OpenBSD$       */
> +
> +/*
> + * Copyright (c) 2020 Martijn van Duren <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/socket.h>
> +
> +#include <errno.h>
> +#include <event.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include "snmpd.h"
> +
> +struct tcp_conn {
> +     int              tc_fd;
> +     char            *tc_rbuf;
> +     size_t           tc_rbuflen;
> +     size_t           tc_rbufsize;
> +     char            *tc_wbuf;
> +     size_t           tc_wbuflen;
> +     size_t           tc_wbufsize;
> +     struct event     tc_rev;
> +     struct event     tc_wev;
> +     struct snmp_conn tc_sc;
> +};
> +
> +#define TCP_MINBUF 8192
> +
> +static const struct timeval  tcp_timeout = { 10, 0 }; /* 10s */
> +
> +void tcp_read(int, short, void *);
> +void tcp_send(struct snmp_conn *, void *, size_t);
> +void tcp_write(int, short, void *);
> +void tcp_free(struct tcp_conn *);
> +
> +void
> +tcp_accept(int lfd, short event, void *arg)
> +{
> +     struct listen_sock      *so = arg;
> +     struct sockaddr_storage  ss;
> +     socklen_t                len = sizeof(ss);
> +     struct tcp_conn         *conn;
> +     int                      fd;
> +
> +     event_add(&so->s_ev, NULL);
> +     evtimer_del(&so->s_evt);
> +     if ((event & EV_TIMEOUT))
> +             return;
> +
> +     if ((fd = accept4(lfd, (struct sockaddr *)&ss, &len,
> +         SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) {
> +             /* Pause accept if we are out of file descriptors  */
> +             if (errno == ENFILE || errno == EMFILE) {
> +                     struct timeval evtpause = { 1, 0 };
> +
> +                     event_del(&so->s_ev);
> +                     evtimer_add(&so->s_evt, &evtpause);
> +             } else if (errno != EAGAIN && errno != EINTR)
> +                     log_debug("%s: accept4", __func__);
> +             return;
> +     }
> +     if ((conn = calloc(1, sizeof(*conn))) == NULL) {
> +             close(fd);
> +             return;
> +     }
> +     conn->tc_fd = fd;
> +     print_host(&ss, conn->tc_sc.sc_host, sizeof(conn->tc_sc.sc_host));
> +     conn->tc_sc.sc_context = conn;
> +
> +     event_set(&(conn->tc_rev), fd, EV_READ, tcp_read, conn);
> +     event_set(&(conn->tc_wev), fd, EV_WRITE, tcp_write, conn);
> +     event_add(&(conn->tc_rev), &tcp_timeout);
> +
> +     TAILQ_INIT(&(conn->tc_sc.sc_messages));
> +
> +     return;
> +}
> +
> +void
> +tcp_read(int fd, short event, void *cookie)
> +{
> +     struct tcp_conn *conn = cookie;
> +     ssize_t          len;
> +     char            *tmpbuf;
> +
> +     if (event == EV_TIMEOUT) {
> +             tcp_free(conn);
> +             return;
> +     }
> +
> +     if (conn->tc_rbufsize - conn->tc_rbuflen == 0) {
> +             tmpbuf = recallocarray(conn->tc_rbuf, conn->tc_rbufsize,
> +                 conn->tc_rbufsize + TCP_MINBUF, 1);
> +             if (tmpbuf == NULL) {
> +                     log_warn("malloc");
> +                     tcp_free(conn);
> +                     return;
> +             }
> +             conn->tc_rbuf = tmpbuf;
> +             conn->tc_rbufsize += TCP_MINBUF;
> +     }
> +
> +     len = read(fd, conn->tc_rbuf + conn->tc_rbuflen,
> +         conn->tc_rbufsize - conn->tc_rbuflen);
> +     if (len <= 0) {
> +             if (errno != EAGAIN && errno != EINTR) {
> +                     tcp_free(conn);
> +                     return;
> +             }
> +             event_add(&(conn->tc_rev), &tcp_timeout);
> +             return;
> +     }
> +
> +     conn->tc_rbuflen += (size_t)len;
> +
> +     while (1) {
> +             len = snmpe_extract(conn->tc_rbuf, conn->tc_rbuflen, tcp_send,
> +                 &(conn->tc_sc));
> +             switch (len) {
> +             case -1:
> +                     tcp_free(conn);
> +                     return;
> +             case 0:
> +                     event_add(&(conn->tc_rev), &tcp_timeout);
> +                     return;
> +             default:
> +                     memmove(conn->tc_rbuf, conn->tc_rbuf + len,
> +                         conn->tc_rbuflen - len);
> +                     conn->tc_rbuflen -= len;
> +             }
> +     }
> +     event_add(&(conn->tc_rev), &tcp_timeout);
> +}
> +
> +void
> +tcp_send(struct snmp_conn *sc_conn, void *buf, size_t buflen)
> +{
> +     struct tcp_conn *conn = sc_conn->sc_context;
> +     char            *tmpbuf;
> +
> +     if (conn->tc_wbufsize - conn->tc_wbuflen < buflen) {
> +             tmpbuf = recallocarray(conn->tc_wbuf, conn->tc_wbufsize,
> +                 conn->tc_wbuflen + buflen, 1);
> +             if (tmpbuf == NULL) {
> +                     log_warn("malloc");
> +                     tcp_free(conn);
> +                     return;
> +             }
> +             conn->tc_wbuf = tmpbuf;
> +             conn->tc_wbufsize = conn->tc_wbuflen + buflen;
> +     }
> +
> +     memcpy(conn->tc_wbuf + conn->tc_wbuflen, buf, buflen);
> +     conn->tc_wbuflen += buflen;
> +
> +     event_add(&(conn->tc_wev), NULL);
> +}
> +
> +void
> +tcp_write(int fd, short event, void *cookie)
> +{
> +     struct tcp_conn *conn = cookie;
> +     ssize_t nwrite;
> +
> +     if ((nwrite = write(conn->tc_fd, conn->tc_wbuf, conn->tc_wbuflen)) == 
> -1) {
> +             if (errno != EAGAIN && errno != EINTR) {
> +                     log_warn("write");
> +                     tcp_free(conn);
> +                     return;
> +             }
> +             event_add(&(conn->tc_wev), NULL);
> +             return;
> +     }
> +
> +     conn->tc_wbuflen -= nwrite;
> +     if (conn->tc_wbuflen > 0) {
> +             memmove(conn->tc_wbuf, conn->tc_wbuf + nwrite,
> +                 conn->tc_wbuflen);
> +             event_add(&(conn->tc_wev), NULL);
> +     }
> +}
> +
> +void
> +tcp_free(struct tcp_conn *conn)
> +{
> +     struct snmp_message *msg;
> +
> +     TAILQ_FOREACH(msg, &(conn->tc_sc.sc_messages), sm_entry)
> +             msg->sm_conn = NULL;
> +     close(conn->tc_fd);
> +
> +     free(conn->tc_rbuf);
> +     free(conn->tc_wbuf);
> +
> +     event_del(&(conn->tc_rev));
> +     event_del(&(conn->tc_wev));
> +
> +     free(conn);
> +}
> Index: udp.c
> ===================================================================
> RCS file: udp.c
> diff -N udp.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ udp.c     15 Feb 2020 11:25:41 -0000
> @@ -0,0 +1,82 @@
> +/*   $OpenBSD$       */
> +
> +/*
> + * Copyright (c) 2020 Martijn van Duren <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/socket.h>
> +
> +#include <event.h>
> +#include <stdlib.h>
> +
> +#include "snmpd.h"
> +
> +struct udp_conn {
> +     int                      uc_fd;
> +     struct sockaddr_storage  uc_lss;
> +     socklen_t                uc_lsslen;
> +     struct sockaddr_storage  uc_rss;
> +     socklen_t                uc_rsslen;
> +     struct snmp_conn         uc_sc;
> +};
> +
> +void udp_send(struct snmp_conn *, void *, size_t);
> +
> +void
> +udp_recvmsg(int fd, short event, void *cookie)
> +{
> +     ssize_t                  nrecv, len;
> +     char                     buf[READ_BUF_SIZE];
> +     struct udp_conn         *conn;
> +
> +     if ((conn = calloc(1, sizeof(*conn))) == NULL)
> +             return;
> +
> +     conn->uc_fd = fd;
> +     conn->uc_sc.sc_context = conn;
> +     conn->uc_rsslen = sizeof(conn->uc_rss);
> +     conn->uc_lsslen = sizeof(conn->uc_lss);
> +     TAILQ_INIT(&(conn->uc_sc.sc_messages));
> +
> +     if ((nrecv = recvfromto(fd, buf, sizeof(buf), 0,
> +         (struct sockaddr *)&conn->uc_rss, &(conn->uc_rsslen),
> +         (struct sockaddr *)&conn->uc_lss, &(conn->uc_lsslen))) < 1) {
> +             free(conn);
> +             return;
> +     }
> +
> +     print_host(&(conn->uc_rss), conn->uc_sc.sc_host,
> +         sizeof(conn->uc_sc.sc_host));
> +     len = snmpe_extract(buf, nrecv, udp_send, &(conn->uc_sc));
> +     if (len <= 0) {
> +             free(conn);
> +             return;
> +     }
> +}
> +
> +void
> +udp_send(struct snmp_conn *sc_conn, void *buf, size_t buflen)
> +{
> +     struct udp_conn *conn = sc_conn->sc_context;
> +     struct snmp_message *msg;
> +
> +     sendtofrom(conn->uc_fd, buf, buflen, 0,
> +         (struct sockaddr *)&(conn->uc_rss), conn->uc_rsslen,
> +         (struct sockaddr *)&(conn->uc_lss), conn->uc_lsslen);
> +
> +     TAILQ_FOREACH(msg, &(conn->uc_sc.sc_messages), sm_entry)
> +             msg->sm_conn = NULL;
> +     free(conn);
> +}
> 

Reply via email to