Index: share/man/man4/packet.4 =================================================================== RCS file: share/man/man4/packet.4 diff -N share/man/man4/packet.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man4/packet.4 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,241 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2024 David Gwynne +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt PACKET 4 +.Os +.Sh NAME +.Nm packet +.Nd packet protocol family +.Sh SYNOPSIS +.Cd "pseudo-device af_packet" +.Pp +.In sys/types.h +.In net/packet.h +.Sh DESCRIPTION +The +.Nm +protocol family provides an interface for sending and receiving low +level network interface packets through the normal +.Xr socket 2 +mechanisms. +.Pp +The +.Nm +protocol family supports the +.Dv SOCK_DGRAM +socket type. +Only root may create +.Nm +protocol family sockets. +.Pp +.Nm +protocol family sockets are designed as an alternative to +.Xr bpf 4 +for handling low data and packet rate communication protocols. +Rather than filtering every packet entering the system before the +network stack like +.Xr bpf 4 , +the +.Nm +protocol family processing avoids this overhead by running after +the built in protocol handlers in the kernel. +For example, it is not possible to handle IPv4 or IPv6 packets +with +.Nm +protocol sockets because the kernel network stack consumes them +before the receive handling for +.Nm +sockets is run. +Currently only Ethernet interfaces are supported. +.Pp +Sockets can be created in the +.Nm +protocol family by using +.Dv AF_PACKET +as the +.Fa domain +argument to +.Xr socket 2 . +The type of interface, as per +.In net/if_types.h , +is specified as the socket +.Fa protocol . +.Pp +Sockets bound to the +.Nm +family use the following address structure: +.Bd -literal -offset indent +#define PACKET_ADDRLEN 8 +#define PACKET_DATALEN 32 + +struct sockaddr_pkt { + uint8_t spkt_len; + uint8_t spkt_family; + uint16_t spkt_proto; + unsigned int spkt_ifindex; + uint8_t spkt_addr[PACKET_ADDRLEN]; + char spkt_ifname[IFNAMSIZ]; + uint8_t spkt_data[PACKET_DATALEN]; +}; +.Ed +.Pp +The interface used for transmitting or receiving packets with a +.Nm +domain socket may be specified by using an interface index with +.Fa spkt_ifindex , +or by name with +.Fa spkt_ifname . +The use of other +.Vt struct sockaddr_pkt +fields depends on the type of interface. +.Ss Ethernet interface packet sockets +A +.Nm +socket for use with Ethernet interfaces can be created using +.Dv IFT_ETHER +as the +.Fa protocol +argument to +.Xr socket 2 : +.Bd -literal -offset indent +int sock = socket(AF_PACKET, SOCK_DGRAM, IFT_ETHERNET); +.Ed +.Pp +The Ethernet protocol is specified with +.Fa spkt_proto +in network byte order. +Ethernet addresses are specified using the first 6 bytes of +.Fa spkt_addr . +.Pp +Ethernet +.Nm +sockets can may receive packets on all interfaces by specifying 0 for +.Va spkt_ifindex +when using +.Xr bind 2 . +Similarly, a +.Dq wildcard +local address of all zeros can be specified in +.Fa spkt_addr . +.Pp +An interface and address must be specified when sending Ethernet packets. +.Pp +Ethernet sockets support the following +.Nm +socket options +using +.Dv IFT_ETHER +as the +.Fa level +argument with +.Xr setsockopt 2 +and +.Xr getsockopt 2 : +.Pp +.Bl -tag -compact +.It Dv PACKET_RECVDSTADDR Ft int +Enable or disable the reception of the Ethernet destination address as a +.Vt struct ether_addr +control message for packets received with +.Xr recvmsg 2 . +.It Dv PACKET_RECVPRIO Ft int +Enable or disable the reception of the mbuf packet priority field as a +.Vt int +sized control message for packets received with +.Xr recvmsg 2 . +.It Dv PACKET_ADD_MEMBERSHIP Ft struct packet_mreq +.It Dv PACKET_DEL_MEMBERSHIP Ft struct packet_mreq +Configure an Ethernet interface to enable or disable reception of +packets destined to the specified multicast Ethernet address. +.Bd -literal -offset indent +struct packet_mreq { + unsigned int pmr_ifindex; + uint8_t pmr_addr[PACKET_ADDRLEN]; + char pmr_ifname[IFNAMSIZ]; +}; +.Ed +.Pp +An interface must be specified using either +.Fa pmr_ifindex +or +.Fa pmr_ifname . +The multicast Ethernet address is specified in the first 6 bytes of +.Fa pmr_addr . +.It Dv PACKET_SENDPRIO Ft int +Specify a mbuf priority value between +.Dv IF_HDRPRIO_MIN +.Pq 0 +and +.Dv IF_HDRPRIO_MAX +.Pq 7 +for packets sent with the Ethernet +.Nm +socket, or +.Dv IF_HDRPRIO_PACKET +to use the existing mbuf priority value. +The default is +.Dv IF_HDRPRIO_PACKET . +.El +.Sh EXAMPLES +To receive LLDP packets on the em0 Ethernet interface: +.Bd -literal -offset indent +struct sockaddr_pkt spkt = { + .spkt_family = AF_PACKET, + .spkt_ifname = "em0", + .spkt_proto = htons(ETHERTYPE_LLDP), +}; +struct packet_mreq pmr = { + .pmr_ifname = "em0", + .pmr_addr = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }, +}; +int sock; + +sock = socket(AF_PACKET, SOCK_DGRAM, IFT_ETHER); +if (sock == -1) + err(1, "ethernet packet socket"); +if (bind(sock, (struct addrinfo *)&spkt, sizeof(spkt)) == -1) + err(1, "bind"); +if (setsockopt(sock, IFT_ETHER, PACKET_ADD_MEMBERSHIP, + &pmr, sizeof(pmr)) == -1) + err(1, "join lldp multicast group"); + +for (;;) { + socklen_t spktlen = sizeof(spkt); + uint8_t pkt[2048]; + ssize_t rv; + + rv = recvfrom(sock, pkt, sizeof(pkt), 0, + (struct sockaddr *)&spkt, &spktlen); + if (rv == -1) + err(1, "lldp recv"); + printf("received %zd bytes from %s", rv, + ether_ntoa((struct ether_addr *)spkt->spkt_addr)); +} +.Ed +.Sh SEE ALSO +.Xr socket 2 , +.Xr netintro 4 +.Sh HISTORY +.Nm +domain sockets appeared in +.Ox 7.7 . +The +.Ox +implementation was influenced by the Linux packet socket interface +and has some similarities to it, but is not compatible. +.Sh AUTHORS +.An David Gwynne Aq Mt dlg@openbsd.org . Index: sys/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/conf/GENERIC,v diff -u -p -r1.299 GENERIC --- sys/conf/GENERIC 3 Oct 2024 04:39:09 -0000 1.299 +++ sys/conf/GENERIC 28 Nov 2024 03:47:00 -0000 @@ -114,5 +115,7 @@ pseudo-device wg # WireGuard pseudo-device bio 1 # ioctl multiplexing device pseudo-device fuse # fuse device + +pseudo-device af_packet # packet (Ethernet) sockets option BOOT_CONFIG # add support for boot -c Index: sys/conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v diff -u -p -r1.741 files --- sys/conf/files 31 Oct 2024 13:55:21 -0000 1.741 +++ sys/conf/files 28 Nov 2024 03:47:00 -0000 @@ -601,6 +601,9 @@ pseudo-device pppx: ifnet pseudo-device vxlan: ifnet, ether, etherbridge pseudo-device wg: ifnet +pseudo-device af_packet +file net/af_packet.c af_packet needs-flag + pseudo-device ksyms file dev/ksyms.c ksyms needs-flag Index: sys/kern/uipc_domain.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_domain.c,v diff -u -p -r1.68 uipc_domain.c --- sys/kern/uipc_domain.c 16 Aug 2024 09:20:34 -0000 1.68 +++ sys/kern/uipc_domain.c 28 Nov 2024 03:47:00 -0000 @@ -41,9 +41,14 @@ #include #include +#include "af_packet.h" #include "bpfilter.h" #include "pflow.h" +#if NAF_PACKET > 0 +extern const struct domain packetdomain; +#endif + const struct domain *const domains[] = { #ifdef MPLS &mplsdomain, @@ -57,6 +62,9 @@ const struct domain *const domains[] = { &inetdomain, &unixdomain, &routedomain, +#if NAF_PACKET > 0 + &packetdomain, +#endif NULL }; @@ -202,9 +210,13 @@ net_sysctl(int *name, u_int namelen, voi if (family == PF_UNSPEC) return (0); - if (family == PF_LINK) - return (net_link_sysctl(name + 1, namelen - 1, oldp, oldlenp, - newp, newlen)); + if (family == PF_LINK) { + int error = net_link_sysctl(name + 1, namelen - 1, + oldp, oldlenp, newp, newlen); + /* let link sockets have a go */ + if (error != ENOPROTOOPT) + return (error); + } if (family == PF_UNIX) return (uipc_sysctl(name + 1, namelen - 1, oldp, oldlenp, newp, newlen)); Index: sys/kern/uipc_socket.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_socket.c,v diff -u -p -r1.345 uipc_socket.c --- sys/kern/uipc_socket.c 8 Nov 2024 21:47:03 -0000 1.345 +++ sys/kern/uipc_socket.c 28 Nov 2024 03:47:00 -0000 @@ -167,6 +167,7 @@ soalloc(const struct protosw *prp, int w case AF_KEY: case AF_ROUTE: case AF_UNIX: + case AF_PACKET: so->so_snd.sb_flags |= SB_MTXLOCK; so->so_rcv.sb_flags |= SB_MTXLOCK; break; Index: sys/net/af_packet.c =================================================================== RCS file: sys/net/af_packet.c diff -N sys/net/af_packet.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/net/af_packet.c 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,62 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2024 David Gwynne + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +const struct domain packetdomain; + +/* reach over to if_ethersubr.c */ +int ether_pkt_ctloutput(int, struct socket *, int, int, struct mbuf *); +extern const struct pr_usrreqs ether_pkt_usrreqs; + +static const struct protosw packetsw[] = { + { + .pr_type = SOCK_DGRAM, + .pr_domain = &packetdomain, + .pr_protocol = IFT_ETHER, + .pr_flags = PR_ATOMIC|PR_ADDR|PR_MPINPUT|PR_MPSOCKET, + + .pr_ctloutput = ether_pkt_ctloutput, + .pr_usrreqs = ðer_pkt_usrreqs, + .pr_sysctl = NULL /* ether_sysctl */, + }, +}; + +const struct domain packetdomain = { + .dom_family = AF_PACKET, + .dom_name = "packet", + .dom_protosw = packetsw, + .dom_protoswNPROTOSW = &packetsw[nitems(packetsw)], +}; + +void +af_packetattach(int n) +{ + /* nop */ +} Index: sys/net/if_aggr.c =================================================================== RCS file: /cvs/src/sys/net/if_aggr.c,v diff -u -p -r1.46 if_aggr.c --- sys/net/if_aggr.c 4 Sep 2024 07:54:52 -0000 1.46 +++ sys/net/if_aggr.c 28 Nov 2024 03:47:00 -0000 @@ -745,15 +745,9 @@ aggr_start(struct ifqueue *ifq) } static inline int -aggr_eh_is_slow(const struct ether_header *eh) +aggr_eh_is_slow(uint64_t etype, uint64_t dst) { - uint64_t dst; - - if (eh->ether_type != htons(ETHERTYPE_SLOW)) - return (0); - - dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost); - return (dst == LACP_ADDR_SLOW_E64); + return (etype == htons(ETHERTYPE_SLOW) && dst == LACP_ADDR_SLOW_E64); } static void @@ -765,13 +759,16 @@ aggr_input(struct ifnet *ifp0, struct mb struct ifnet *ifp = &sc->sc_if; struct ether_header *eh; int hlen = sizeof(*eh); + uint64_t dst; if (!ISSET(ifp->if_flags, IFF_RUNNING)) goto drop; eh = mtod(m, struct ether_header *); + dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost); + if (!ISSET(m->m_flags, M_VLANTAG) && - __predict_false(aggr_eh_is_slow(eh))) { + __predict_false(aggr_eh_is_slow(eh->ether_type, dst))) { unsigned int rx_proto = AGGR_PROTO_RX_LACP; struct ether_slowproto_hdr *sph; int drop = 0; @@ -812,6 +809,12 @@ aggr_input(struct ifnet *ifp0, struct mb default: break; } + } + + if (!ISSET(m->m_flags, M_VLANTAG) && + __predict_false(dst == 0x0180c200000e)) { + p->p_input(ifp0, m); + return; } if (__predict_false(!p->p_collecting)) Index: sys/net/if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v diff -u -p -r1.293 if_ethersubr.c --- sys/net/if_ethersubr.c 14 Feb 2024 22:41:48 -0000 1.293 +++ sys/net/if_ethersubr.c 28 Nov 2024 03:47:00 -0000 @@ -140,6 +140,14 @@ didn't get a copy, you may request one f #include #endif /* MPLS */ +#include "af_packet.h" +#if NAF_PACKET > 0 +#include + +static struct mbuf * + ether_pkt_input(struct ifnet *, struct mbuf *, uint64_t, uint16_t); +#endif + /* #define ETHERDEBUG 1 */ #ifdef ETHERDEBUG int etherdebug = ETHERDEBUG; @@ -578,6 +586,9 @@ ether_input(struct ifnet *ifp, struct mb return; #endif default: +#if NAF_PACKET > 0 + m = ether_pkt_input(ifp, m, dst, etype); +#endif goto dropanyway; } @@ -1247,3 +1258,927 @@ ether_extract_headers(struct mbuf *m0, s ext->tcp ? "tcp," : "", ext->udp ? "udp," : "", ext->iplen, ext->iphlen, ext->tcphlen, ext->paylen); } + +#if NAF_PACKET > 0 + +#include +#include +#include + +/* + * lock order is: + * + * - socket lock + * - ether_pcb_lock + * - socket buffer mtx + */ + +struct ether_pcb; + +struct ether_pcb_group { + TAILQ_ENTRY(ether_pcb_group) + epg_entry; + struct ether_pcb * + epg_pcb; + unsigned int epg_ifindex; + uint8_t epg_addr[ETHER_ADDR_LEN]; + struct task epg_hook; +}; + +TAILQ_HEAD(ether_pcb_groups, ether_pcb_group); + +struct ether_pcb { + TAILQ_ENTRY(ether_pcb) + ep_entry; + struct rwlock ep_lock; + + struct socket *ep_socket; + + uint64_t ep_laddr; + uint64_t ep_faddr; + unsigned int ep_ifindex; + uint16_t ep_etype; + + uint64_t ep_options; + int ep_txprio; + + struct ether_pcb_groups + ep_groups; +}; + +TAILQ_HEAD(ether_pcb_list, ether_pcb); + +static int ether_pkt_attach(struct socket *, int, int); +static int ether_pkt_detach(struct socket *); +static int ether_pkt_bind(struct socket *, struct mbuf *, struct proc *); +static int ether_pkt_connect(struct socket *, struct mbuf *); +static int ether_pkt_disconnect(struct socket *); +static int ether_pkt_shutdown(struct socket *); +static int ether_pkt_send(struct socket *, struct mbuf *, struct mbuf *, + struct mbuf *); +static int ether_pkt_control(struct socket *, u_long, caddr_t, + struct ifnet *); +static int ether_pkt_sockaddr(struct socket *, struct mbuf *); +static int ether_pkt_peeraddr(struct socket *, struct mbuf *); + +const struct pr_usrreqs ether_pkt_usrreqs = { + .pru_attach = ether_pkt_attach, + .pru_detach = ether_pkt_detach, + .pru_bind = ether_pkt_bind, + .pru_connect = ether_pkt_connect, + .pru_disconnect = ether_pkt_disconnect, + .pru_shutdown = ether_pkt_shutdown, + .pru_send = ether_pkt_send, + .pru_control = ether_pkt_control, + .pru_sockaddr = ether_pkt_sockaddr, + .pru_peeraddr = ether_pkt_peeraddr, +}; + +static struct rwlock ether_pcb_lock = RWLOCK_INITIALIZER("ethsocks"); +static struct ether_pcb_list ether_pcbs = TAILQ_HEAD_INITIALIZER(ether_pcbs); + +static int +ether_pkt_valid_etype(uint16_t etype) +{ + switch (etype) { + case ETHERTYPE_LLDP: + case ETHERTYPE_EAPOL: + return (1); + } + + return (0); +} + +static int +ether_pkt_nam2spkt(struct sockaddr_pkt **spktp, const struct mbuf *nam) +{ + struct sockaddr_pkt *spkt; + + if (nam->m_len != sizeof(*spkt)) + return (EINVAL); + + spkt = mtod(nam, struct sockaddr_pkt *); + if (spkt->spkt_family != AF_PACKET) + return (EAFNOSUPPORT); + *spktp = spkt; + return (0); +} + +static int +ether_pkt_ifp(struct ifnet **ifpp, const struct sockaddr_pkt *spkt) +{ + struct ifnet *ifp; + + if (spkt->spkt_ifindex != 0) + ifp = if_get(spkt->spkt_ifindex); + else if (spkt->spkt_ifname[0] != '\0') { + KERNEL_LOCK(); + ifp = if_unit(spkt->spkt_ifname); + KERNEL_UNLOCK(); + } else { + *ifpp = NULL; + return (0); + } + + if (ifp == NULL) + return (ENXIO); + + if (ifp->if_type != IFT_ETHER) { + if_put(ifp); + return (EAFNOSUPPORT); + } + + *ifpp = ifp; + return (0); +} + +static int +ether_pkt_attach(struct socket *so, int proto, int wait) +{ + struct ether_pcb *ep; + int error; + + if (so->so_pcb != NULL) + return (EINVAL); + + error = suser(curproc); + if (error != 0) + return (error); + + error = soreserve(so, MCLBYTES, MCLBYTES); + if (error != 0) + return (error); + + ep = malloc(sizeof(*ep), M_PCB, (wait ? M_WAITOK : M_NOWAIT) | M_ZERO); + if (ep == NULL) + return (ENOMEM); + + rw_init(&ep->ep_lock, "ethsock"); + + so->so_pcb = ep; + ep->ep_socket = so; /* shares a ref with the list */ + + ep->ep_txprio = IF_HDRPRIO_PACKET; + TAILQ_INIT(&ep->ep_groups); + + /* give the ref to the list */ + rw_enter_write(ðer_pcb_lock); + TAILQ_INSERT_TAIL(ðer_pcbs, ep, ep_entry); + rw_exit_write(ðer_pcb_lock); + + return (0); +} + +static int +ether_pkt_detach(struct socket *so) +{ + struct ether_pcb *ep; + struct ether_pcb_group *epg, *nepg; + struct ifnet *ifp; + + soassertlocked(so); + + ep = so->so_pcb; + + /* take the ref from the list */ + rw_enter_write(ðer_pcb_lock); + TAILQ_REMOVE(ðer_pcbs, ep, ep_entry); + rw_exit_write(ðer_pcb_lock); + + so->so_pcb = NULL; /* shares a ref with the list */ + + /* XXX locking */ + TAILQ_FOREACH_SAFE(epg, &ep->ep_groups, epg_entry, nepg) { + ifp = if_get(epg->epg_ifindex); + if (ifp != NULL) { + struct ifreq ifr; + struct sockaddr *sa; + + if_detachhook_del(ifp, &epg->epg_hook); + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifp->if_xname, + sizeof(ifr.ifr_name)); + sa = &ifr.ifr_addr; + sa->sa_family = AF_UNSPEC; + memcpy(sa->sa_data, &epg->epg_addr, ETHER_ADDR_LEN); + + (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); + } + if_put(ifp); + + TAILQ_REMOVE(&ep->ep_groups, epg, epg_entry); + free(epg, M_PCB, sizeof(*epg)); + } + + free(ep, M_PCB, sizeof(*ep)); + + return (0); +} + +static int +ether_pkt_bind(struct socket *so, struct mbuf *nam, struct proc *p) +{ + struct sockaddr_pkt *spkt; + struct ether_pcb *ep; + struct ether_pcb *epe; + struct ifnet *ifp = NULL; + unsigned int ifindex = 0; + uint16_t etype; + uint64_t laddr; + int error; + + soassertlocked(so); + + error = ether_pkt_nam2spkt(&spkt, nam); + if (error != 0) + return (error); + + etype = ntohs(spkt->spkt_proto); + if (!ether_pkt_valid_etype(etype)) + return (EADDRNOTAVAIL); + + ep = so->so_pcb; + if (ep->ep_etype != 0) + return (EINVAL); + + error = ether_pkt_ifp(&ifp, spkt); + if (error != 0) + return (error); + if (ifp != NULL) + ifindex = ifp->if_index; + + laddr = ether_addr_to_e64((struct ether_addr *)spkt->spkt_addr); + + rw_enter_write(ðer_pcb_lock); + TAILQ_FOREACH(epe, ðer_pcbs, ep_entry) { + if (ep == epe) + continue; + + /* XXX check stuff */ + } + + if (error == 0) { + /* serialised by the socket lock */ + ep->ep_etype = etype; + ep->ep_ifindex = ifindex; + ep->ep_laddr = laddr; + } + rw_exit_write(ðer_pcb_lock); + + if_put(ifp); + return (error); +} + +static int +ether_pkt_connect(struct socket *so, struct mbuf *nam) +{ + struct sockaddr_pkt *spkt; + struct ether_pcb *ep; + struct ether_pcb *epe; + struct ifnet *ifp = NULL; + uint64_t faddr; + uint16_t etype; + int error; + + soassertlocked(so); + + error = ether_pkt_nam2spkt(&spkt, nam); + if (error != 0) + return (error); + + etype = ntohs(spkt->spkt_proto); + if (!ether_pkt_valid_etype(etype)) + return (EADDRNOTAVAIL); + + faddr = ether_addr_to_e64((struct ether_addr *)spkt->spkt_addr); + if (faddr == 0) + return (EADDRNOTAVAIL); + + error = ether_pkt_ifp(&ifp, spkt); + if (error != 0) + return (error); + if (ifp == NULL) + return (EADDRNOTAVAIL); + + ep = so->so_pcb; + if (ep->ep_etype != 0) { + if (ep->ep_faddr != 0 || + ep->ep_etype != etype) { + error = EISCONN; + goto put; + } + } + if (ep->ep_ifindex != 0) { + if (ep->ep_ifindex != ifp->if_index) { + error = EADDRNOTAVAIL; + goto put; + } + } + + rw_enter_write(ðer_pcb_lock); + TAILQ_FOREACH(epe, ðer_pcbs, ep_entry) { + if (ep == epe) + continue; + /* XXX check stuff */ + } + + if (error == 0) { + /* serialised by the socket lock */ + ep->ep_etype = etype; + ep->ep_ifindex = ifp->if_index; + ep->ep_faddr = faddr; + } + rw_exit_write(ðer_pcb_lock); + +put: + if_put(ifp); + return (error); +} + +static int +ether_pkt_disconnect(struct socket *so) +{ + struct ether_pcb *ep; + + soassertlocked(so); + + ep = so->so_pcb; + if (ep->ep_faddr == 0) + return (ENOTCONN); + + rw_enter_write(ðer_pcb_lock); + ep->ep_ifindex = 0; + ep->ep_etype = 0; + ep->ep_laddr = 0; + ep->ep_faddr = 0; + rw_exit_write(ðer_pcb_lock); + + return (0); +} + +static int +ether_pkt_shutdown(struct socket *so) +{ + soassertlocked(so); + socantsendmore(so); + return (0); +} + +static int +ether_pkt_send(struct socket *so, struct mbuf *m, struct mbuf *nam, + struct mbuf *control) +{ + struct ether_pcb *ep; + int error; + uint16_t etype; + uint64_t laddr; + uint64_t faddr; + struct ifnet *ifp = NULL; + struct arpcom *ac; + struct ether_header *eh; + int txprio; + + soassertlocked_readonly(so); + + ep = so->so_pcb; + KASSERTMSG(ep != NULL, "%s: NULL pcb on socket %p", __func__, so); + txprio = ep->ep_txprio; + + /* XXX get prio out of a cmsg */ + m_freem(control); + + if (nam != NULL) { + struct sockaddr_pkt *spkt; + + error = ether_pkt_nam2spkt(&spkt, nam); + if (error != 0) + goto drop; + + etype = ntohs(spkt->spkt_proto); + if (!ether_pkt_valid_etype(etype)) { + error = EADDRNOTAVAIL; + goto drop; + } + + if (ep->ep_faddr != 0) { + error = EISCONN; + goto drop; + } + faddr = ether_addr_to_e64((struct ether_addr *)spkt->spkt_addr); + if (faddr == 0) { + error = EADDRNOTAVAIL; + goto drop; + } + + error = ether_pkt_ifp(&ifp, spkt); + if (error != 0) + goto drop; + if (ifp == NULL) { + ifp = if_get(ep->ep_ifindex); + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto drop; + } + } else { + if (ep->ep_ifindex != 0 && + ep->ep_ifindex != ifp->if_index) { + error = EADDRNOTAVAIL; + goto drop; + } + } + + if (ep->ep_etype != etype) { + if (ep->ep_etype == 0) { + /* this is cheeky */ + rw_enter_write(ðer_pcb_lock); + ep->ep_etype = etype; + rw_exit_write(ðer_pcb_lock); + } else { + error = EADDRNOTAVAIL; + goto drop; + } + } + } else { + faddr = ep->ep_faddr; + if (faddr == 0) { + error = ENOTCONN; + goto drop; + } + + ifp = if_get(ep->ep_ifindex); + if (ifp == NULL) { + error = ENXIO; + goto drop; + } + + etype = ep->ep_etype; + } + + if (ifp->if_type != IFT_ETHER) { + error = EAFNOSUPPORT; + goto drop; + } + + ac = (struct arpcom *)ifp; + + laddr = ether_addr_to_e64((struct ether_addr *)ac->ac_enaddr); + if (ep->ep_laddr != laddr) { + if (ep->ep_laddr != 0) { + error = EADDRNOTAVAIL; + goto drop; + } + } + + m = m_prepend(m, ETHER_ALIGN + sizeof(*eh), M_NOWAIT); + if (m == NULL) + goto drop; + m_adj(m, ETHER_ALIGN); + + if (txprio != IF_HDRPRIO_PACKET) + m->m_pkthdr.pf.prio = txprio; + + eh = mtod(m, struct ether_header *); + ether_e64_to_addr((struct ether_addr *)eh->ether_dhost, faddr); + ether_e64_to_addr((struct ether_addr *)eh->ether_shost, laddr); + eh->ether_type = htons(etype); + + error = if_enqueue(ifp, m); + m = NULL; + +drop: + if_put(ifp); + m_freem(m); + return (error); +} + +static int +ether_pkt_control(struct socket *so, u_long cmd, caddr_t data, + struct ifnet *ifp) +{ + return (EOPNOTSUPP); +} + +static int +ether_pkt_sockaddr_pkt(struct ether_pcb *ep, struct mbuf *nam, uint64_t addr) +{ + struct sockaddr_pkt *spkt; + struct ifnet *ifp; + + nam->m_len = sizeof(*spkt); + spkt = mtod(nam, struct sockaddr_pkt *); + memset(spkt, 0, sizeof(*spkt)); + spkt->spkt_len = sizeof(*spkt); + spkt->spkt_family = AF_PACKET; + + ether_e64_to_addr((struct ether_addr *)spkt->spkt_addr, addr); + + if (ep->ep_etype) { + spkt->spkt_proto = htons(ep->ep_etype); + spkt->spkt_ifindex = ep->ep_ifindex; + + ifp = if_get(ep->ep_ifindex); + if (ifp != NULL) { + strlcpy(spkt->spkt_ifname, ifp->if_xname, + sizeof(spkt->spkt_ifname)); + } + if_put(ifp); + } + + return (0); +} + +static int +ether_pkt_sockaddr(struct socket *so, struct mbuf *nam) +{ + struct ether_pcb *ep = so->so_pcb; + + return (ether_pkt_sockaddr_pkt(ep, nam, ep->ep_laddr)); +} + +static int +ether_pkt_peeraddr(struct socket *so, struct mbuf *nam) +{ + struct ether_pcb *ep = so->so_pcb; + + return (ether_pkt_sockaddr_pkt(ep, nam, ep->ep_faddr)); +} + +static void +ether_pkt_group_detach(void *arg) +{ + struct ether_pcb_group *epg = arg; + struct ether_pcb *ep = epg->epg_pcb; + struct socket *so = ep->ep_socket; + struct ifnet *ifp; + + ifp = if_get(epg->epg_ifindex); + + /* XXX locking^Wreference counts */ + solock(so); + if (ifp != NULL) + if_detachhook_del(ifp, &epg->epg_hook); + TAILQ_REMOVE(&ep->ep_groups, epg, epg_entry); + sounlock(so); + + if_put(ifp); + free(epg, M_PCB, sizeof(*epg)); +} + +static int +ether_pkt_group(struct socket *so, int optname, struct mbuf *m) +{ + struct packet_mreq *pmr; + struct ifreq ifr; + struct sockaddr *sa; + struct ifnet *ifp; + struct ether_pcb *ep; + struct ether_pcb_group *epg; + u_long cmd; + int error; + + soassertlocked(so); + + if (m->m_len != sizeof(*pmr)) + return (EINVAL); + + pmr = mtod(m, struct packet_mreq *); + if (!ETHER_IS_MULTICAST(pmr->pmr_addr)) + return (EADDRNOTAVAIL); + + if (pmr->pmr_ifindex == 0) { + KERNEL_LOCK(); + ifp = if_unit(pmr->pmr_ifname); + KERNEL_UNLOCK(); + } else + ifp = if_get(pmr->pmr_ifindex); + if (ifp == NULL) + return (ENXIO); + + if (ifp->if_type != IFT_ETHER) { + error = EADDRNOTAVAIL; + goto put; + } + + if (ETHER_IS_BROADCAST(pmr->pmr_addr)) { + error = 0; + goto put; + } + + ep = so->so_pcb; + TAILQ_FOREACH(epg, &ep->ep_groups, epg_entry) { + if (epg->epg_ifindex != ifp->if_index) + continue; + if (!ETHER_IS_EQ(epg->epg_addr, pmr->pmr_addr)) + continue; + + break; + } + + switch (optname) { + case PACKET_ADD_MEMBERSHIP: + if (epg != NULL) { + error = EISCONN; + goto put; + } + epg = malloc(sizeof(*epg), M_PCB, M_DONTWAIT); + if (epg == NULL) { + error = ENOMEM; + goto put; + } + + epg->epg_pcb = ep; + epg->epg_ifindex = ifp->if_index; + memcpy(&epg->epg_addr, pmr->pmr_addr, sizeof(epg->epg_addr)); + task_set(&epg->epg_hook, ether_pkt_group_detach, epg); + + cmd = SIOCADDMULTI; + break; + case PACKET_DEL_MEMBERSHIP: + if (epg == NULL) { + error = ENOTCONN; + goto put; + } + cmd = SIOCDELMULTI; + break; + default: + panic("%s: unexpected optname %d", __func__, optname); + /* NOTREACHED */ + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); + sa = &ifr.ifr_addr; + sa->sa_family = AF_UNSPEC; + memcpy(sa->sa_data, pmr->pmr_addr, ETHER_ADDR_LEN); + + /* XXX soref? */ + /* this could lead to multiple epgs for the same if/group */ + sounlock(so); + KERNEL_LOCK(); + NET_LOCK(); + error = (*ifp->if_ioctl)(ifp, cmd, (caddr_t)&ifr); + NET_UNLOCK(); + KERNEL_UNLOCK(); + solock(so); + + switch (optname) { + case PACKET_ADD_MEMBERSHIP: + if (error != 0) { + free(epg, M_PCB, sizeof(*epg)); + break; + } + + TAILQ_INSERT_TAIL(&ep->ep_groups, epg, epg_entry); + if_detachhook_add(ifp, &epg->epg_hook); + break; + case PACKET_DEL_MEMBERSHIP: + if (error != 0) + break; + + if_detachhook_del(ifp, &epg->epg_hook); + TAILQ_REMOVE(&ep->ep_groups, epg, epg_entry); + free(epg, M_PCB, sizeof(*epg)); + break; + } +put: + if_put(ifp); + + return (error); +} + +#define ETHER_PCB_OPTM(_v) (1ULL << (_v)) + +#define ETHER_PCB_OPTS \ + ETHER_PCB_OPTM(PACKET_RECVDSTADDR) | \ + ETHER_PCB_OPTM(PACKET_RECVPRIO) + +static int +ether_pkt_setopt(struct ether_pcb *ep, int optname, struct mbuf *m) +{ + uint64_t optm = ETHER_PCB_OPTM(optname); + int opt; + + if (!ISSET(ETHER_PCB_OPTS, optm)) + return (ENOPROTOOPT); + + if (m->m_len != sizeof(opt)) + return (EINVAL); + + opt = *mtod(m, int *); + if (opt) + SET(ep->ep_options, optm); + else + CLR(ep->ep_options, optm); + + return (0); +} + +static int +ether_pkt_setsockopt(struct socket *so, int optname, struct mbuf *m) +{ + struct ether_pcb *ep = so->so_pcb; + int error = ENOPROTOOPT; + int v; + + if (optname >= 0 && optname < 64) + return (ether_pkt_setopt(ep, optname, m)); + + switch (optname) { + case PACKET_ADD_MEMBERSHIP: + case PACKET_DEL_MEMBERSHIP: + error = ether_pkt_group(so, optname, m); + break; + case PACKET_SENDPRIO: + if (m->m_len != sizeof(v)) { + error = EINVAL; + break; + } + v = *mtod(m, int *); + error = if_txhprio_l2_check(v); + if (error != 0) + break; + ep->ep_txprio = v; + break; + + default: + break; + } + + return (error); +} + +static int +ether_pkt_getopt(struct ether_pcb *ep, int optname, struct mbuf *m) +{ + uint64_t optm = ETHER_PCB_OPTM(optname); + int opt; + + if (!ISSET(ETHER_PCB_OPTS, optm)) + return (ENOPROTOOPT); + + opt = !!ISSET(ep->ep_options, optm); + + m->m_len = sizeof(opt); + *mtod(m, int *) = opt; + + return (0); +} + +static int +ether_pkt_getsockopt(struct socket *so, int optname, struct mbuf *m) +{ + struct ether_pcb *ep = so->so_pcb; + int error = ENOPROTOOPT; + + if (optname >= 0 && optname < 64) + return (ether_pkt_getopt(ep, optname, m)); + + switch (optname) { + default: + break; + } + + return (error); +} + +int +ether_pkt_ctloutput(int op, struct socket *so, int level, int optname, + struct mbuf *m) +{ + int error = 0; + + if (level != IFT_ETHER) + return (EINVAL); + + switch (op) { + case PRCO_SETOPT: + error = ether_pkt_setsockopt(so, optname, m); + break; + case PRCO_GETOPT: + error = ether_pkt_getsockopt(so, optname, m); + break; + } + + return (error); +} + +static struct mbuf * +ether_pkt_cmsg(struct mbuf *cmsgs, const void *data, size_t datalen, + int type, int level) +{ + struct mbuf *cm; + + cm = sbcreatecontrol(data, datalen, type, level); + if (cm != NULL) { + cm->m_next = cmsgs; + cmsgs = cm; + } + + return (cmsgs); +} + +static void +ether_pkt_recv(struct socket *so, struct mbuf *m0, + const struct sockaddr_pkt *spkt) +{ + struct ether_pcb *ep = so->so_pcb; + struct mbuf *m; + struct mbuf *cmsgs = NULL; + int ok; + + /* offset 0 and m_adj cos sbappendaddr needs m_pkthdr.len */ + m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT); + if (m == NULL) + return; + m_adj(m, sizeof(struct ether_header)); + + if (ISSET(ep->ep_options, ETHER_PCB_OPTM(PACKET_RECVPRIO))) { + int rxprio = m0->m_pkthdr.pf.prio; + cmsgs = ether_pkt_cmsg(cmsgs, &rxprio, sizeof(rxprio), + PACKET_RECVPRIO, IFT_ETHER); + } + + if (ISSET(ep->ep_options, ETHER_PCB_OPTM(PACKET_RECVDSTADDR))) { + struct ether_header *eh = mtod(m0, struct ether_header *); + cmsgs = ether_pkt_cmsg(cmsgs, eh->ether_dhost, ETHER_ADDR_LEN, + PACKET_RECVDSTADDR, IFT_ETHER); + } + + if (ISSET(so->so_options, SO_TIMESTAMP)) { + struct timeval tv; + m_microtime(m0, &tv); + cmsgs = ether_pkt_cmsg(cmsgs, &tv, sizeof(tv), + SCM_TIMESTAMP, SOL_SOCKET); + } + + mtx_enter(&so->so_rcv.sb_mtx); + ok = sbappendaddr(so, &so->so_rcv, (struct sockaddr *)spkt, m, cmsgs); + mtx_leave(&so->so_rcv.sb_mtx); + + if (!ok) { + m_freem(m); + m_freem(cmsgs); + return; + } + + sorwakeup(so); +} + +static struct mbuf * +ether_pkt_input(struct ifnet *ifp, struct mbuf *m, uint64_t dst, uint16_t etype) +{ + struct sockaddr_pkt spkt = { .spkt_family = AF_UNSPEC }; + struct ether_pcb *ep; + struct ether_header *eh; + uint64_t src; + + if (TAILQ_EMPTY(ðer_pcbs)) + return (m); + + eh = mtod(m, struct ether_header *); + src = ether_addr_to_e64((struct ether_addr *)eh->ether_shost); + if (src == 0) + return (m); + + rw_enter_read(ðer_pcb_lock); + TAILQ_FOREACH(ep, ðer_pcbs, ep_entry) { + if (ep->ep_etype == 0) /* bound? */ + continue; + if (ep->ep_etype != etype) + continue; + if (ep->ep_ifindex != 0) { + if (ep->ep_ifindex != ifp->if_index) + continue; + } + + if (ep->ep_laddr != 0) { + if (ep->ep_laddr != dst) + continue; + } + /* ether_input says dst is valid for local delivery */ + + if (ep->ep_faddr != 0) { /* connected? */ + if (ep->ep_faddr != src) + continue; + } + + if (spkt.spkt_family == AF_UNSPEC) { + spkt.spkt_len = sizeof(spkt); + spkt.spkt_family = AF_PACKET; + spkt.spkt_proto = htons(etype); + spkt.spkt_ifindex = ifp->if_index; + ether_e64_to_addr((struct ether_addr *)spkt.spkt_addr, + src); + strlcpy(spkt.spkt_ifname, ifp->if_xname, + sizeof(spkt.spkt_ifname)); + } + + ether_pkt_recv(ep->ep_socket, m, &spkt); + } + rw_exit_read(ðer_pcb_lock); + + return (m); +} + +#endif /* NAF_PACKET */ Index: sys/net/packet.h =================================================================== RCS file: sys/net/packet.h diff -N sys/net/packet.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/net/packet.h 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,51 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2024 David Gwynne + * + * 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. + */ + +#ifndef _NET_PACKET_H_ +#define _NET_PACKET_H_ + +/* + * link sockets + */ + +#define PACKET_ADDRLEN 8 /* big enough for Ethernet */ +#define PACKET_DATALEN 32 + +struct sockaddr_pkt { + uint8_t spkt_len; + uint8_t spkt_family; /* AF_PACKET */ + uint16_t spkt_proto; + unsigned int spkt_ifindex; + uint8_t spkt_addr[PACKET_ADDRLEN]; + char spkt_ifname[IFNAMSIZ]; + uint8_t spkt_data[PACKET_DATALEN]; +}; + +#define PACKET_RECVDSTADDR 0 /* int */ +#define PACKET_RECVPRIO 1 /* int */ +#define PACKET_ADD_MEMBERSHIP 64 /* struct packet_mreq */ +#define PACKET_DEL_MEMBERSHIP 65 /* struct packet_mreq */ +#define PACKET_SENDPRIO 66 /* int: IF_HDRPRIO_{MIN-MAX,PACKET} */ + +struct packet_mreq { + unsigned int pmr_ifindex; + uint8_t pmr_addr[PACKET_ADDRLEN]; + char pmr_ifname[IFNAMSIZ]; +}; + +#endif /* _NET_PACKET_H_ */ Index: sys/sys/socket.h =================================================================== RCS file: /cvs/src/sys/sys/socket.h,v diff -u -p -r1.105 socket.h --- sys/sys/socket.h 3 Sep 2022 21:13:48 -0000 1.105 +++ sys/sys/socket.h 28 Nov 2024 03:47:00 -0000 @@ -200,7 +200,8 @@ struct splice { #define AF_MPLS 33 /* MPLS */ #define pseudo_AF_PFLOW 34 /* pflow */ #define pseudo_AF_PIPEX 35 /* PIPEX */ -#define AF_MAX 36 +#define AF_PACKET 36 /* packet (Ethernet) sockets */ +#define AF_MAX 37 /* * Structure used by kernel to store most @@ -284,6 +285,7 @@ struct sockproto { #define PF_MPLS AF_MPLS #define PF_PFLOW pseudo_AF_PFLOW #define PF_PIPEX pseudo_AF_PIPEX +#define PF_PACKET AF_PACKET #define PF_MAX AF_MAX /* Index: usr.sbin/lldpctl/Makefile =================================================================== RCS file: usr.sbin/lldpctl/Makefile diff -N usr.sbin/lldpctl/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpctl/Makefile 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,8 @@ +PROG= lldpctl +SRCS= lldpctl.c +MAN= + +CFLAGS+= -Wall -Werror +DEBUG= -g + +.include Index: usr.sbin/lldpctl/lldpctl.c =================================================================== RCS file: usr.sbin/lldpctl/lldpctl.c diff -N usr.sbin/lldpctl/lldpctl.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpctl/lldpctl.c 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,113 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2024 David Gwynne + * + * 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 +#include +#include + +#include /* IFNAMSIZ */ +#include + +#include +#include /* ether_ntoa */ + +#include +#include +#include + +static void hexdump(const void *, size_t); + +int +main(int argc, char *argv[]) +{ + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + .sun_path = "/var/run/lldpd.sock", + }; + int s; + + s = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (s == -1) + err(1, "socket"); + + if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "%s connect", sun.sun_path); + + for (;;) { + struct sockaddr_pkt spkt; + char buf[2048]; + struct iovec iov[2] = { + { &spkt, sizeof(spkt) }, + { buf, sizeof(buf) }, + }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + ssize_t rv; + + rv = recvmsg(s, &msg, 0); + if (rv == -1) + err(1, "recv"); + if (rv == 0) + break; + + if (rv < sizeof(spkt)) { + printf("too short for spkt\n"); + continue; + } + + rv -= sizeof(spkt); + printf("%s nei %s\n", spkt.spkt_ifname, + ether_ntoa((struct ether_addr *)spkt.spkt_addr)); + hexdump(buf, rv); + } + + return (0); +} + +static int +printable(int ch) +{ + if (ch == '\0') + return ('_'); + if (!isprint(ch)) + return ('~'); + + return (ch); +} + +static void +hexdump(const void *d, size_t datalen) +{ + const uint8_t *data = d; + size_t i, j = 0; + + for (i = 0; i < datalen; i += j) { + printf("%4zu: ", i); + for (j = 0; j < 16 && i+j < datalen; j++) + printf("%02x ", data[i + j]); + while (j++ < 16) + printf(" "); + printf("|"); + for (j = 0; j < 16 && i+j < datalen; j++) + putchar(printable(data[i + j])); + printf("|\n"); + } +} + Index: usr.sbin/lldpd/Makefile =================================================================== RCS file: usr.sbin/lldpd/Makefile diff -N usr.sbin/lldpd/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpd/Makefile 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,12 @@ +PROG= lldpd +SRCS= lldpd.c +SRCS+= log.c +MAN= + +CFLAGS+= -Wall -Werror +DEBUG= -g + +LDADD+= -levent +DPADD= ${LIBEVENT} + +.include Index: usr.sbin/lldpd/lldpd.c =================================================================== RCS file: usr.sbin/lldpd/lldpd.c diff -N usr.sbin/lldpd/lldpd.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpd/lldpd.c 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,701 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2024 David Gwynne + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "log.h" + +int rdaemon(int); + +#define LLDPD_USER "_lldpd" +#define LLDPD_CTL_PATH "/var/run/lldpd.sock" + +#define CMSG_FOREACH(_cmsg, _msgp) \ + for ((_cmsg) = CMSG_FIRSTHDR((_msgp)); \ + (_cmsg) != NULL; \ + (_cmsg) = CMSG_NXTHDR((_msgp), (_cmsg))) + +static const uint8_t maddr[ETHER_ADDR_LEN] = + { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }; + +static inline int +cmsg_match(const struct cmsghdr *cmsg, size_t len, int level, int type) +{ + return (cmsg->cmsg_len == CMSG_LEN(len) && + cmsg->cmsg_level == level && cmsg->cmsg_type == type); +} + +#define CMSG_MATCH(_cmsg, _len, _level, _type) \ + cmsg_match((_cmsg), (_len), (_level), (_type)) + +struct iface; + +struct lldp_nei { + struct iface *nei_iface; + struct ether_addr nei_faddr; + struct ether_addr nei_laddr; + + TAILQ_ENTRY(lldp_nei) nei_entry; /* iface list */ + TAILQ_ENTRY(lldp_nei) nei_dentry; /* lldpd list */ + unsigned int nei_refs; + + void * *nei_msg; + size_t nei_msglen; + +}; + +TAILQ_HEAD(lldp_neighbors, lldp_nei); + +struct iface_key { + unsigned int if_index; + char if_name[IFNAMSIZ]; +}; + +struct iface { + struct iface_key if_key; /* must be first */ + RBT_ENTRY(iface) if_entry; + + struct lldp_neighbors if_neighbors; +}; + +RBT_HEAD(ifaces, iface); + +static inline int + +iface_cmp(const struct iface *a, const struct iface *b) +{ + const struct iface_key *ka = &a->if_key; + const struct iface_key *kb = &b->if_key; + + if (ka->if_index > kb->if_index) + return (1); + if (ka->if_index < kb->if_index) + return (-1); + return (0); +} + +RBT_PROTOTYPE(ifaces, iface, if_entry, iface_cmp); + +struct lldpd_ctl { + struct lldpd *ctl_lldpd; + + struct event ctl_rd_ev; + struct event ctl_wr_ev; + + struct lldp_nei *ctl_nei; /* cursor */ +}; + +struct lldpd { + const char *ctl_path; + + struct event rt_ev; + struct event en_ev; + struct event ctl_ev; + + struct ifaces ifaces; + + struct lldp_neighbors neighbors; +}; + +static void rtsock_open(struct lldpd *); +static void rtsock_recv(int, short, void *); +static void ensock_open(struct lldpd *); +static void ensock_recv(int, short, void *); +static void ctlsock_open(struct lldpd *); +static void ctlsock_accept(int, short, void *); + +static int getall(struct lldpd *); + +extern char *__progname; + +__dead static void +usage(void) +{ + fprintf(stderr, "usage: %s [-d] [-s /path/to/ctl.sock]\n", __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct lldpd _lldpd = { + .ctl_path = LLDPD_CTL_PATH, + .ifaces = RBT_INITIALIZER(_lldpd.ifaces), + .neighbors = TAILQ_HEAD_INITIALIZER(_lldpd.neighbors), + }; + struct lldpd *lldpd = &_lldpd; /* let me use -> consistently */ + struct passwd *pw; + int debug = 0; + int devnull = -1; + + int ch; + + while ((ch = getopt(argc, argv, "ds:")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 's': + lldpd->ctl_path = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + if (geteuid() != 0) + errx(1, "need root privileges"); + + pw = getpwnam(LLDPD_USER); + if (pw == NULL) + errx(1, "no %s user", LLDPD_USER); + + if (!debug) { + logger_syslog(__progname, LOG_DAEMON); + devnull = open(_PATH_DEVNULL, O_RDWR); + if (devnull == -1) + err(1, "%s", _PATH_DEVNULL); + } + + rtsock_open(lldpd); + ensock_open(lldpd); + ctlsock_open(lldpd); + + printf("debug = %d\n", debug); + + if (chroot(pw->pw_dir) == -1) + err(1, "chroot %s", pw->pw_dir); + if (chdir("/") == -1) + err(1, "chdir %s", pw->pw_dir); + + /* drop privs */ + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + errx(1, "can't drop privileges"); + + pw = NULL; + endpwent(); + + if (getall(lldpd) == -1) + warn("getall"); + + if (!debug && rdaemon(devnull) == -1) + err(1, "unable to daemonize"); + + event_init(); + + event_set(&lldpd->rt_ev, EVENT_FD(&lldpd->rt_ev), EV_READ|EV_PERSIST, + rtsock_recv, lldpd); + event_set(&lldpd->en_ev, EVENT_FD(&lldpd->en_ev), EV_READ|EV_PERSIST, + ensock_recv, lldpd); + event_set(&lldpd->ctl_ev, EVENT_FD(&lldpd->ctl_ev), EV_READ|EV_PERSIST, + ctlsock_accept, lldpd); + + event_add(&lldpd->rt_ev, NULL); + event_add(&lldpd->en_ev, NULL); + event_add(&lldpd->ctl_ev, NULL); + + event_dispatch(); + + return (0); +} + +static void +rtsock_open(struct lldpd *lldpd) +{ + unsigned int rtfilter; + int s; + + s = socket(AF_ROUTE, SOCK_RAW | SOCK_NONBLOCK, AF_UNSPEC); + if (s == -1) + err(1, "route socket"); + + rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE); + if (setsockopt(s, AF_ROUTE, ROUTE_MSGFILTER, + &rtfilter, sizeof(rtfilter)) == -1) + err(1, "route socket setsockopt msgfilter"); + + event_set(&lldpd->rt_ev, s, 0, NULL, NULL); +} + +static void +rtsock_recv(int s, short events, void *arg) +{ + char buf[1024]; + ssize_t rv; + + rv = recv(s, buf, sizeof(buf), 0); + if (rv == -1) { + lwarn("route message"); + return; + } + + linfo("route message: %zd bytes", rv); +} + +static void +ensock_open(struct lldpd *lldpd) +{ + struct sockaddr_pkt spkt = { + .spkt_family = AF_PACKET, + .spkt_proto = htons(ETHERTYPE_LLDP), + }; + int opt; + int s; + + s = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, IFT_ETHER); + if (s == -1) + err(1, "Ethernet socket"); + + opt = 1; + if (setsockopt(s, IFT_ETHER, PACKET_RECVDSTADDR, + &opt, sizeof(opt)) == -1) + err(1, "Ethernet setsockopt recv dstaddr"); + + if (bind(s, (struct sockaddr *)&spkt, sizeof(spkt)) == -1) + err(1, "Ethernet bind lldp"); + + event_set(&lldpd->en_ev, s, 0, NULL, NULL); +} + +static void +ensock_recv(int s, short events, void *arg) +{ + struct lldpd *lldpd = arg; + struct sockaddr_pkt spkt; + uint8_t buf[1500]; + + struct ether_addr *ea = NULL; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + uint8_t buf[CMSG_SPACE(sizeof(*ea))]; + } cmsgbuf; + struct iovec iov[1] = { + { .iov_base = buf, .iov_len = sizeof(buf) }, + }; + struct msghdr msg = { + .msg_name = &spkt, + .msg_namelen = sizeof(spkt), + .msg_control = &cmsgbuf.buf, + .msg_controllen = sizeof(cmsgbuf.buf), + .msg_iov = iov, + .msg_iovlen = 1, + }; + ssize_t rv; + size_t len; + + struct iface *ifp; + struct iface_key key; + struct lldp_nei *nei; + + rv = recvmsg(s, &msg, 0); + if (rv == -1) { + lwarn("Ethernet recv"); + return; + } + + CMSG_FOREACH(cmsg, &msg) { + if (CMSG_MATCH(cmsg, + sizeof(*ea), IFT_ETHER, PACKET_RECVDSTADDR)) { + ea = (struct ether_addr *)CMSG_DATA(cmsg); + } + } + + printf("%zd bytes from %02x:%02x:%02x:%02x:%02x:%02x", rv, + spkt.spkt_addr[0], spkt.spkt_addr[1], spkt.spkt_addr[2], + spkt.spkt_addr[3], spkt.spkt_addr[4], spkt.spkt_addr[5]); + if (ea != NULL) { + printf(" to %02x:%02x:%02x:%02x:%02x:%02x", + ea->ether_addr_octet[0], ea->ether_addr_octet[1], + ea->ether_addr_octet[2], ea->ether_addr_octet[3], + ea->ether_addr_octet[4], ea->ether_addr_octet[5]); + } + printf(" on %s\n", spkt.spkt_ifname); + + key.if_index = spkt.spkt_ifindex; + ifp = RBT_FIND(ifaces, &lldpd->ifaces, (struct iface *)&key); + if (ifp == NULL) { + /* count */ + return; + } + + TAILQ_FOREACH(nei, &ifp->if_neighbors, nei_entry) { + if (memcmp(&nei->nei_faddr, spkt.spkt_addr, + sizeof(nei->nei_faddr)) == 0) + break; + } + + if (nei == NULL) { + nei = malloc(sizeof(*nei)); + if (nei == NULL) { + lwarn("interface %s: neighbor alloc", + ifp->if_key.if_name); + /* count drop */ + return; + } + nei->nei_iface = ifp; + memcpy(&nei->nei_faddr, spkt.spkt_addr, + sizeof(nei->nei_faddr)); + nei->nei_msg = NULL; + nei->nei_msglen = 0; + + nei->nei_refs = 1; + TAILQ_INSERT_TAIL(&ifp->if_neighbors, nei, nei_entry); + TAILQ_INSERT_TAIL(&lldpd->neighbors, nei, nei_dentry); + } + + len = rv; + if (len > nei->nei_msglen) { + void *msg = realloc(nei->nei_msg, len); + if (msg == NULL) { + lwarn("interface %s; pdu alloc", + ifp->if_key.if_name); + if (nei->nei_msg == NULL) { + TAILQ_REMOVE(&lldpd->neighbors, nei, + nei_dentry); + TAILQ_REMOVE(&ifp->if_neighbors, nei, + nei_entry); + free(nei); + } + /* count drop */ + return; + } + nei->nei_msg = msg; + } + + nei->nei_msglen = len; + memcpy(nei->nei_msg, buf, len); +} + +static void +ctlsock_open(struct lldpd *lldpd) +{ + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + }; + const char *path = lldpd->ctl_path; + mode_t oumask; + int s; + + if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >= + sizeof(sun.sun_path)) + errc(ENAMETOOLONG, 1, "control socket %s", path); + + s = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0); + if (s == -1) + err(1, "control socket"); + + /* try connect first? */ + + if (unlink(path) == -1) { + if (errno != ENOENT) + err(1, "control socket %s unlink", path); + } + + oumask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (oumask == -1) + err(1, "umask"); + if (bind(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "control socket %s bind", path); + if (umask(oumask) == -1) + err(1, "umask restore"); + + if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) + err(1, "control socket %s chmod", path); + + if (listen(s, 5) == -1) + err(1, "control socket %s listen", path); + + event_set(&lldpd->ctl_ev, s, 0, NULL, NULL); +} + +static struct lldp_nei * +lldp_nei_take(struct lldpd *lldpd, struct lldp_nei *nei) +{ + nei->nei_refs++; + return (nei); +} + +static void +lldp_nei_rele(struct lldpd *lldpd, struct lldp_nei *nei) +{ + if (--nei->nei_refs == 0) { + TAILQ_REMOVE(&lldpd->neighbors, nei, nei_dentry); + free(nei); + } +} + +static void +ctl_close(struct lldpd *lldpd, struct lldpd_ctl *ctl) +{ + int fd = EVENT_FD(&ctl->ctl_rd_ev); + struct lldp_nei *nei = ctl->ctl_nei; + + if (nei != NULL) + lldp_nei_rele(lldpd, nei); + event_del(&ctl->ctl_rd_ev); + event_del(&ctl->ctl_wr_ev); + free(ctl); + close(fd); +} + +static void +ctl_recv(int fd, short events, void *arg) +{ + struct lldpd_ctl *ctl = arg; + struct lldpd *lldpd = ctl->ctl_lldpd; + uint8_t buf[1024]; + ssize_t rv; + + rv = recv(fd, buf, sizeof(buf), 0); + if (rv == -1) { + lwarn("ctl recv"); + return; + } + if (rv == 0) { + ctl_close(lldpd, ctl); + return; + } + + linfo("%s: %zd bytes", __func__, rv); +} + +static void +ctl_send(int fd, short events, void *arg) +{ + struct lldpd_ctl *ctl = arg; + struct lldpd *lldpd = ctl->ctl_lldpd; + struct lldp_nei *nei = ctl->ctl_nei; + struct iface *ifp; + struct lldp_nei *nnei; + ssize_t rv; + + struct sockaddr_pkt spkt = { + .spkt_family = AF_PACKET, + .spkt_proto = htons(ETHERTYPE_LLDP), + }; + struct iovec iov[2] = { { &spkt, sizeof(spkt) } }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + ifp = nei->nei_iface; + if (ifp != NULL) { + spkt.spkt_ifindex = ifp->if_key.if_index; + strlcpy(spkt.spkt_ifname, ifp->if_key.if_name, + sizeof(spkt.spkt_ifname)); + } + memcpy(spkt.spkt_addr, &nei->nei_faddr, sizeof(nei->nei_faddr)); + + iov[1].iov_base = nei->nei_msg; + iov[1].iov_len = nei->nei_msglen; + + rv = sendmsg(fd, &msg, 0); + if (rv == -1) { + lwarn("ctl send"); + return; + } + + nnei = TAILQ_NEXT(nei, nei_dentry); + if (nnei == NULL) { + ctl_close(lldpd, ctl); + return; + } + ctl->ctl_nei = lldp_nei_take(lldpd, nnei); + lldp_nei_rele(lldpd, nei); + + event_add(&ctl->ctl_wr_ev, NULL); +} + +static void +ctlsock_accept(int s, short events, void *arg) +{ + struct lldpd *lldpd = arg; + struct lldpd_ctl *ctl; + struct lldp_nei *nei; + int fd; + + fd = accept4(s, NULL, NULL, SOCK_NONBLOCK); + if (fd == -1) { + lwarn("control socket %s accept", lldpd->ctl_path); + return; + } + + linfo("%s: hi", __func__); + + nei = TAILQ_FIRST(&lldpd->neighbors); + if (nei == NULL) { + close(fd); + return; + } + linfo("%s: hihi", __func__); + + ctl = malloc(sizeof(*ctl)); + if (ctl == NULL) { + lwarn("ctl alloc"); + close(fd); + return; + } + ctl->ctl_lldpd = lldpd; + linfo("%s: hihihi", __func__); + + event_set(&ctl->ctl_rd_ev, fd, EV_READ|EV_PERSIST, + ctl_recv, ctl); + event_set(&ctl->ctl_wr_ev, fd, EV_WRITE, + ctl_send, ctl); + + ctl->ctl_nei = lldp_nei_take(lldpd, nei); + ctl_send(fd, EV_WRITE, ctl); +} + +static int +getall(struct lldpd *lldpd) +{ + struct ifaddrs *ifa0, *ifa; + struct sockaddr_dl *sdl; + struct if_data *ifi; + struct iface *ifp; + struct packet_mreq pmr; + + if (getifaddrs(&ifa0) == -1) + return (-1); + + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + ifi = ifa->ifa_data; + if (ifi->ifi_type != IFT_ETHER) + continue; + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_index == 0) { + warnx("interface %s has index 0, skipping", + ifa->ifa_name); + continue; + } + + printf("%s index %u\n", ifa->ifa_name, sdl->sdl_index); + + ifp = malloc(sizeof(*ifp)); + if (ifp == NULL) { + warn("interface %s allocation", ifa->ifa_name); + continue; + } + + ifp->if_key.if_index = sdl->sdl_index; + strlcpy(ifp->if_key.if_name, ifa->ifa_name, + sizeof(ifp->if_key.if_name)); + + TAILQ_INIT(&ifp->if_neighbors); + + if (RBT_INSERT(ifaces, &lldpd->ifaces, ifp) != NULL) { + warnx("interface %s: index %u already exists", + ifa->ifa_name, ifp->if_key.if_index); + free(ifp); + } + + memset(&pmr, 0, sizeof(pmr)); + pmr.pmr_ifindex = ifp->if_key.if_index; + memcpy(pmr.pmr_addr, maddr, ETHER_ADDR_LEN); + + if (setsockopt(EVENT_FD(&lldpd->en_ev), + IFT_ETHER, PACKET_ADD_MEMBERSHIP, + &pmr, sizeof(pmr)) == -1) + warn("interface %s: add membership", ifa->ifa_name); + } + + freeifaddrs(ifa0); + + return (0); +} + +RBT_GENERATE(ifaces, iface, if_entry, iface_cmp); + +/* daemon(3) clone, intended to be used in a "r"estricted environment */ +int +rdaemon(int devnull) +{ + if (devnull == -1) { + errno = EBADF; + return (-1); + } + if (fcntl(devnull, F_GETFL) == -1) + return (-1); + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + (void)dup2(devnull, STDIN_FILENO); + (void)dup2(devnull, STDOUT_FILENO); + (void)dup2(devnull, STDERR_FILENO); + if (devnull > 2) + (void)close(devnull); + + return (0); +} Index: usr.sbin/lldpd/log.c =================================================================== RCS file: usr.sbin/lldpd/log.c diff -N usr.sbin/lldpd/log.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpd/log.c 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,150 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static const struct __logger conslogger = { + err, + errx, + warn, + warnx, + warnx, /* info */ + warnx /* debug */ +}; + +__dead static void syslog_err(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead static void syslog_errx(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +static void syslog_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +static void syslog_vstrerror(int, int, const char *, va_list) + __attribute__((__format__ (printf, 3, 0))); + +static const struct __logger syslogger = { + syslog_err, + syslog_errx, + syslog_warn, + syslog_warnx, + syslog_info, + syslog_debug +}; + +const struct __logger *__logger = &conslogger; + +void +logger_syslog(const char *progname, int facility) +{ + openlog(progname, LOG_PID | LOG_NDELAY, facility); + tzset(); + + __logger = &syslogger; +} + +static void +syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) == -1) { + syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); + exit(1); + } + + syslog(priority, "%s: %s", s, strerror(e)); + + free(s); +} + +static void +syslog_err(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_errx(int ecode, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_CRIT, fmt, ap); + va_end(ap); + + exit(ecode); +} + +static void +syslog_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + syslog_vstrerror(errno, LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +} + +static void +syslog_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_INFO, fmt, ap); + va_end(ap); +} + +static void +syslog_debug(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); +} Index: usr.sbin/lldpd/log.h =================================================================== RCS file: usr.sbin/lldpd/log.h diff -N usr.sbin/lldpd/log.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lldpd/log.h 28 Nov 2024 03:47:00 -0000 @@ -0,0 +1,47 @@ + +/* + * Copyright (c) 2008 David Gwynne + * + * 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. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +struct __logger { + __dead void (*err)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + __dead void (*errx)(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); + void (*warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*warnx)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + void (*debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +}; + +extern const struct __logger *__logger; + +#define lerr(_e, _f...) __logger->err((_e), _f) +#define lerrx(_e, _f...) __logger->errx((_e), _f) +#define lwarn(_f...) __logger->warn(_f) +#define lwarnx(_f...) __logger->warnx(_f) +#define linfo(_f...) __logger->info(_f) +#define ldebug(_f...) __logger->debug(_f) + +void logger_syslog(const char *, int); + +#endif /* _LOG_H_ */