summaryrefslogtreecommitdiff
path: root/usr.sbin/pppoe/server.c
diff options
context:
space:
mode:
authorJason Wright <jason@cvs.openbsd.org>2000-06-18 07:30:42 +0000
committerJason Wright <jason@cvs.openbsd.org>2000-06-18 07:30:42 +0000
commit2150d33fb6cf1dfe1016cbf7782271645f323685 (patch)
treea63b5f9c629d99e6f6e3e99b7e00fb61855ed3c2 /usr.sbin/pppoe/server.c
parenta9a8db41f99fc2b2bd057e21ca6bec6059a826af (diff)
import my pppoe code
Diffstat (limited to 'usr.sbin/pppoe/server.c')
-rw-r--r--usr.sbin/pppoe/server.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/usr.sbin/pppoe/server.c b/usr.sbin/pppoe/server.c
new file mode 100644
index 00000000000..4ac0a85d419
--- /dev/null
+++ b/usr.sbin/pppoe/server.c
@@ -0,0 +1,478 @@
+/* $OpenBSD: server.c,v 1.1 2000/06/18 07:30:41 jason Exp $ */
+
+/*
+ * Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Network Security
+ * Technologies, Inc.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/bpf.h>
+#include <net/ppp_defs.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <stdlib.h>
+#include <md5.h>
+
+#include "pppoe.h"
+
+#define COOKIE_LEN 4 /* bytes/cookie, must be <= 16 */
+#define COOKIE_MAX 16
+
+static char ac_cookie_key[8];
+
+static void getpackets __P((int, char *, struct ether_addr *));
+
+static void recv_padi __P((int, struct ether_addr *,
+ struct ether_header *, struct pppoe_header *, u_long, u_int8_t *));
+static void recv_padr __P((int, char *, struct ether_addr *,
+ struct ether_header *, struct pppoe_header *, u_long, u_int8_t *));
+static void recv_padt __P((int, struct ether_addr *,
+ struct ether_header *, struct pppoe_header *, u_long, u_int8_t *));
+
+static void send_pado __P((int, struct ether_addr *,
+ struct ether_header *, struct pppoe_header *, u_long, u_int8_t *));
+static void send_pads __P((int, char *, struct ether_addr *,
+ struct ether_header *, struct pppoe_header *, u_long, u_int8_t *));
+static void key_gen __P((void));
+static u_int8_t *key_make __P((u_int8_t *, int, u_int8_t *, int));
+static int key_cmp __P((u_int8_t *, int, u_int8_t *, int, u_int8_t *, int));
+
+void
+server_mode(bpffd, sysname, srvname, ea)
+ int bpffd;
+ char *sysname, *srvname;
+ struct ether_addr *ea;
+{
+ struct pppoe_session *ses;
+ fd_set rfds;
+ int n;
+
+ key_gen();
+
+ while (1) {
+reselect:
+ FD_ZERO(&rfds);
+ FD_SET(bpffd, &rfds);
+ n = bpffd;
+ ses = LIST_FIRST(&session_master.sm_sessions);
+ while (ses) {
+ if (ses->s_fd != -1) {
+ FD_SET(ses->s_fd, &rfds);
+ if (ses->s_fd > n)
+ n = ses->s_fd;
+ }
+ ses = LIST_NEXT(ses, s_next);
+ }
+
+ n = select(n+1, &rfds, NULL, NULL, NULL);
+ if (n < 0) {
+ if (errno == EINTR)
+ goto reselect;
+ err(EX_IOERR, "select");
+ return;
+ }
+ if (n == 0)
+ continue;
+ if (FD_ISSET(bpffd, &rfds)) {
+ n--;
+ getpackets(bpffd, sysname, ea);
+ }
+ if (n == 0)
+ continue;
+
+ ses = LIST_FIRST(&session_master.sm_sessions);
+ while (ses) {
+ if (ses->s_fd != -1 && FD_ISSET(ses->s_fd, &rfds)) {
+ if (ppp_to_bpf(bpffd, ses->s_fd, ea,
+ &ses->s_ea, ses->s_id) < 0) {
+ send_padt(bpffd, ea,
+ &ses->s_ea, ses->s_id);
+ session_destroy(ses);
+ }
+ n--;
+ if (n == 0)
+ break;
+ }
+ ses = LIST_NEXT(ses, s_next);
+ }
+ }
+}
+
+void
+key_gen()
+{
+ u_int32_t r;
+
+ r = arc4random();
+ memcpy(ac_cookie_key, &r, sizeof(r));
+ r = arc4random();
+ memcpy(ac_cookie_key + sizeof(r), &r, sizeof(r));
+}
+
+u_int8_t *
+key_make(in1, in1len, in2, in2len)
+ u_int8_t *in1, *in2;
+ int in1len, in2len;
+{
+ u_int8_t *p;
+ MD5_CTX ctx;
+
+ p = (u_int8_t *)malloc(COOKIE_MAX);
+ if (p == NULL)
+ return (p);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, in1, in1len);
+ MD5Update(&ctx, in2, in2len);
+ MD5Final(p, &ctx);
+ return (p);
+}
+
+int
+key_cmp(k, klen, in1, in1len, in2, in2len)
+ u_int8_t *k, *in1, *in2;
+ int klen, in1len, in2len;
+{
+ u_int8_t *p;
+ int r;
+
+ if (klen != COOKIE_LEN)
+ return (-1);
+
+ p = key_make(in1, in1len, in2, in2len);
+ if (p == NULL)
+ return (-1);
+
+ r = memcmp(k, p, COOKIE_LEN);
+ free(p);
+ return (r);
+}
+
+static void
+getpackets(bpffd, sysname, ea)
+ int bpffd;
+ char *sysname;
+ struct ether_addr *ea;
+{
+ static u_int8_t *pktbuf;
+ u_int8_t *mpkt, *pkt, *epkt;
+ struct ether_header eh;
+ struct pppoe_header ph;
+ struct bpf_hdr *bh;
+ int rlen;
+ u_long len;
+
+ if (pktbuf == NULL) {
+ pktbuf = (u_int8_t *)malloc(PPPOE_BPF_BUFSIZ);
+ if (pktbuf == NULL)
+ return;
+ }
+
+ rlen = read(bpffd, pktbuf, PPPOE_BPF_BUFSIZ);
+ if (rlen < 0)
+ return;
+
+ pkt = pktbuf;
+ epkt = pkt + rlen;
+ while (pkt < epkt) {
+ bh = (struct bpf_hdr *)pkt;
+ len = bh->bh_caplen;
+ mpkt = pkt + bh->bh_hdrlen;
+
+ /* Pull out ethernet header */
+ if (len < sizeof(struct ether_header))
+ goto next;
+ bcopy(mpkt, &eh, sizeof(struct ether_header));
+ eh.ether_type = ntohs(eh.ether_type);
+ len -= sizeof(struct ether_header);
+ mpkt += sizeof(struct ether_header);
+
+ /* Pull out pppoe header */
+ if (len < sizeof(struct pppoe_header))
+ goto next;
+ bcopy(mpkt, &ph, sizeof(struct pppoe_header));
+ mpkt += sizeof(struct pppoe_header);
+ len -= sizeof(struct pppoe_header);
+ ph.len = ntohs(ph.len);
+ ph.sessionid = ntohs(ph.sessionid);
+
+ if (PPPOE_VER(ph.vertype) != 1 ||
+ PPPOE_TYPE(ph.vertype) != 1)
+ goto next;
+
+ if (len > ph.len)
+ len = ph.len;
+
+ if (eh.ether_type == ETHERTYPE_PPPOEDISC) {
+ /* Discovery Stage */
+ switch (ph.code) {
+ case PPPOE_CODE_PADI:
+ recv_padi(bpffd, ea, &eh, &ph, len, mpkt);
+ break;
+ case PPPOE_CODE_PADR:
+ recv_padr(bpffd, sysname, ea, &eh, &ph,
+ len, mpkt);
+ break;
+ case PPPOE_CODE_PADT:
+ recv_padt(bpffd, ea, &eh, &ph, len, mpkt);
+ break;
+ default:
+ recv_debug(bpffd, ea, &eh, &ph, len, mpkt);
+ }
+ }
+ else if (eh.ether_type == ETHERTYPE_PPPOE) {
+ /* Session Stage */
+ struct pppoe_session *s;
+
+ s = session_find_eaid(
+ (struct ether_addr *)&eh.ether_shost[0],
+ ph.sessionid);
+ if (s != NULL && bpf_to_ppp(s->s_fd, len, mpkt) <= 0)
+ session_destroy(s);
+ }
+next:
+ pkt += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
+ }
+}
+
+static void
+recv_padi(bpffd, ea, eh, ph, pktlen, pktbuf)
+ int bpffd;
+ struct ether_addr *ea;
+ struct ether_header *eh;
+ struct pppoe_header *ph;
+ u_long pktlen;
+ u_int8_t *pktbuf;
+{
+ struct tag_list tl;
+
+ if (ph->sessionid != 0)
+ return;
+ if (bcmp(&eh->ether_dhost[0], etherbroadcastaddr, ETHER_ADDR_LEN))
+ return;
+
+ tag_init(&tl);
+ if (tag_pkt(&tl, pktlen, pktbuf) < 0)
+ goto out;
+
+ if (tag_lookup(&tl, PPPOE_TAG_SERVICE_NAME, 1) != NULL)
+ goto out;
+
+ send_pado(bpffd, ea, eh, ph, pktlen, pktbuf);
+
+out:
+ tag_destroy(&tl);
+}
+
+static void
+send_pado(bpffd, ea, eh, ph, pktlen, pktbuf)
+ int bpffd;
+ struct ether_addr *ea;
+ struct ether_header *eh;
+ struct pppoe_header *ph;
+ u_long pktlen;
+ u_int8_t *pktbuf;
+{
+ struct pppoe_tag ktag, htag;
+ char hn[MAXHOSTNAMELEN];
+ u_int8_t *k = NULL;
+ struct iovec v[7];
+ int idx = 0;
+
+ memcpy(&eh->ether_dhost[0], &eh->ether_shost[0], ETHER_ADDR_LEN);
+ memcpy(&eh->ether_shost[0], ea, ETHER_ADDR_LEN);
+ eh->ether_type = htons(eh->ether_type);
+ v[idx].iov_base = eh; v[idx].iov_len = sizeof(*eh); idx++;
+
+ ph->code = PPPOE_CODE_PADO;
+ v[idx].iov_base = ph; v[idx].iov_len = sizeof(*ph); idx++;
+
+ v[idx].iov_base = pktbuf; v[idx].iov_len = pktlen; idx++;
+
+ if (gethostname(hn, sizeof(hn)) < 0)
+ return;
+ htag.len = strlen(hn);
+ htag.type = htons(PPPOE_TAG_AC_NAME);
+ htag.val = hn;
+ v[idx].iov_base = &htag;
+ v[idx].iov_len = sizeof(htag.len) + sizeof(htag.type);
+ idx++;
+ v[idx].iov_base = hn; v[idx].iov_len = htag.len; idx++;
+ ph->len += sizeof(htag.len) + sizeof(htag.type) + htag.len;
+ htag.len = htons(htag.len);
+
+ k = key_make(&eh->ether_dhost[0], ETHER_ADDR_LEN, ac_cookie_key,
+ sizeof(ac_cookie_key));
+ if (k == NULL)
+ return;
+ ktag.type = htons(PPPOE_TAG_AC_COOKIE);
+ ktag.len = COOKIE_LEN;
+ ktag.val = k;
+ v[idx].iov_base = &ktag;
+ v[idx].iov_len = sizeof(ktag.len) + sizeof(ktag.type);
+ idx++;
+ v[idx].iov_base = k; v[idx].iov_len = COOKIE_LEN; idx++;
+ ph->len += sizeof(ktag.len) + sizeof(ktag.type) + COOKIE_LEN;
+ ktag.len = htons(COOKIE_LEN);
+
+ ph->len = htons(ph->len);
+
+ writev(bpffd, v, idx);
+
+ if (k)
+ free(k);
+}
+
+static void
+recv_padr(bpffd, sysname, ea, eh, ph, pktlen, pktbuf)
+ int bpffd;
+ char *sysname;
+ struct ether_addr *ea;
+ struct ether_header *eh;
+ struct pppoe_header *ph;
+ u_long pktlen;
+ u_int8_t *pktbuf;
+{
+ struct tag_list tl;
+ struct tag_node *n;
+
+ if (ph->sessionid != 0)
+ return;
+
+ tag_init(&tl);
+ if (tag_pkt(&tl, pktlen, pktbuf) < 0)
+ return;
+
+ n = tag_lookup(&tl, PPPOE_TAG_AC_COOKIE, 0);
+ if (n == NULL)
+ return;
+ if (key_cmp(n->val, n->len, &eh->ether_shost[0], ETHER_ADDR_LEN,
+ ac_cookie_key, sizeof(ac_cookie_key)))
+ return;
+
+ send_pads(bpffd, sysname, ea, eh, ph, pktlen, pktbuf);
+
+ tag_destroy(&tl);
+}
+
+static void
+send_pads(bpffd, sysname, ea, eh, ph, pktlen, pktbuf)
+ int bpffd;
+ char *sysname;
+ struct ether_addr *ea;
+ struct ether_header *eh;
+ struct pppoe_header *ph;
+ u_long pktlen;
+ u_int8_t *pktbuf;
+{
+ char hn[MAXHOSTNAMELEN];
+ struct iovec v[16];
+ struct pppoe_session *s;
+ struct pppoe_tag htag;
+ int idx = 0;
+
+ s = session_new((struct ether_addr *)&eh->ether_shost[0]);
+ if (s == NULL)
+ return;
+
+ memcpy(&eh->ether_dhost[0], &eh->ether_shost[0], ETHER_ADDR_LEN);
+ memcpy(&eh->ether_shost[0], ea, ETHER_ADDR_LEN);
+ eh->ether_type = htons(eh->ether_type);
+ v[idx].iov_base = eh; v[idx].iov_len = sizeof(*eh); idx++;
+
+ ph->code = PPPOE_CODE_PADS;
+ ph->sessionid = htons(s->s_id);
+ if (gethostname(hn, sizeof(hn)) < 0)
+ return;
+ v[idx].iov_base = ph; v[idx].iov_len = sizeof(*ph); idx++;
+
+ v[idx].iov_base = pktbuf; v[idx].iov_len = pktlen; idx++;
+
+ htag.len = strlen(hn);
+ htag.type = htons(PPPOE_TAG_AC_NAME);
+ htag.val = hn;
+ v[idx].iov_base = &htag;
+ v[idx].iov_len = sizeof(htag.len) + sizeof(htag.type);
+ idx++;
+ v[idx].iov_base = hn; v[idx].iov_len = htag.len; idx++;
+ ph->len += sizeof(htag.len) + sizeof(htag.type) + htag.len;
+ htag.len = htons(htag.len);
+
+ ph->len = htons(ph->len);
+
+ writev(bpffd, v, idx);
+
+ s->s_fd = runppp(bpffd, sysname);
+ if (s->s_fd < 0) {
+ /* XXX Send PADT with Generic-Error */
+ s->s_fd = -1;
+ }
+}
+
+static void
+recv_padt(bpffd, ea, eh, ph, pktlen, pktbuf)
+ int bpffd;
+ struct ether_addr *ea;
+ struct ether_header *eh;
+ struct pppoe_header *ph;
+ u_long pktlen;
+ u_int8_t *pktbuf;
+{
+ struct pppoe_session *s;
+ struct tag_list tl;
+
+ tag_init(&tl);
+ if (tag_pkt(&tl, pktlen, pktbuf) < 0)
+ goto out;
+
+ s = session_find_eaid((struct ether_addr *)&eh->ether_shost[0],
+ ph->sessionid);
+ if (s == NULL)
+ goto out;
+ session_destroy(s);
+
+out:
+ tag_destroy(&tl);
+}