* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
- * $Id: ppp.c,v 1.9 1997/03/04 03:31:35 paulus Exp $
+ * $Id: ppp.c,v 1.10 1997/04/30 05:44:43 paulus Exp $
*/
/*
#define ETHERTYPE_IP 0x800
#endif
-#ifndef PPP_MAXMTU
-#define PPP_MAXMTU 65535
-#endif
-
extern time_t time;
/*
static int ppplwsrv __P((queue_t *));
#ifndef NO_DLPI
static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *));
-static void dlpi_error __P((queue_t *, int, int, int));
+static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int));
static void dlpi_ok __P((queue_t *, int));
#endif
static int send_data __P((mblk_t *, upperstr_t *));
static int putctl2 __P((queue_t *, int, int, int));
static int putctl4 __P((queue_t *, int, int, int));
static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound));
#define PPP_ID 0xb1a6
static struct module_info ppp_info = {
break;
}
#ifdef NO_DLPI
- if (!pass_packet(us->ppa, mp, 1)) {
- freemsg(mp);
+ if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
break;
- }
#endif
if (!send_data(mp, us))
putq(q, mp);
if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
break;
n = *(int *)mp->b_cont->b_rptr;
- if (n <= 0 || n > PPP_MAXMTU)
+ if (n <= 0 || n > PPP_MAXMRU)
break;
if (n < PPP_MRU)
n = PPP_MRU;
n = *(int *)mp->b_cont->b_rptr;
if (n <= 0 || n > PPP_MAXMTU)
break;
- if (n < PPP_MRU)
- n = PPP_MRU;
us->mtu = n;
#ifdef LACHTCP
+ /* The MTU reported in netstat, not used as IP max packet size! */
us->ifstats.ifs_mtu = n;
#endif
if (us->lowerq)
error = 0;
break;
+ case SIOCSIFMTU:
+ /*
+ * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather
+ * they take the MTU from the DL_INFO_ACK we sent in response
+ * to their DL_INFO_REQ. Fortunately, they will update the
+ * MTU if we send an unsolicited DL_INFO_ACK up.
+ */
+ if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
+ mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
+ dlpi_request(q, mq, us);
+ error = 0;
+ break;
+
case SIOCGIFNETMASK:
case SIOCSIFNETMASK:
case SIOCGIFADDR:
reply->b_wptr += sizeof(dl_info_ack_t);
bzero((caddr_t) info, sizeof(dl_info_ack_t));
info->dl_primitive = DL_INFO_ACK;
- info->dl_max_sdu = PPP_MAXMTU;
+ info->dl_max_sdu = us->ppa->mtu;
info->dl_min_sdu = 1;
info->dl_addr_length = sizeof(ulong);
#ifdef DL_OTHER
if (size < sizeof(dl_attach_req_t))
goto badprim;
if (us->state != DL_UNATTACHED || us->ppa != 0) {
- dlpi_error(q, DL_ATTACH_REQ, DL_OUTSTATE, 0);
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0);
break;
}
for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
if (ppa->ppa_id == d->attach_req.dl_ppa)
break;
if (ppa == 0) {
- dlpi_error(q, DL_ATTACH_REQ, DL_BADPPA, 0);
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0);
break;
}
us->ppa = ppa;
if (size < sizeof(dl_detach_req_t))
goto badprim;
if (us->state != DL_UNBOUND || us->ppa == 0) {
- dlpi_error(q, DL_DETACH_REQ, DL_OUTSTATE, 0);
+ dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0);
break;
}
qwriter(q, mp, detach_ppa, PERIM_OUTER);
if (size < sizeof(dl_bind_req_t))
goto badprim;
if (us->state != DL_UNBOUND || us->ppa == 0) {
- dlpi_error(q, DL_BIND_REQ, DL_OUTSTATE, 0);
+ dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0);
break;
}
+#if 0
+ /* apparently this test fails (unnecessarily?) on some systems */
if (d->bind_req.dl_service_mode != DL_CLDLS) {
- dlpi_error(q, DL_BIND_REQ, DL_UNSUPPORTED, 0);
+ dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0);
break;
}
+#endif
/* saps must be valid PPP network protocol numbers,
except that we accept ETHERTYPE_IP in place of PPP_IP. */
if (sap == ETHERTYPE_IP)
sap = PPP_IP;
if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) {
- dlpi_error(q, DL_BIND_REQ, DL_BADADDR, 0);
+ dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
break;
}
if (os->sap == sap)
break;
if (os != 0) {
- dlpi_error(q, DL_BIND_REQ, DL_NOADDR, 0);
+ dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0);
break;
}
if (size < sizeof(dl_unbind_req_t))
goto badprim;
if (us->state != DL_IDLE) {
- dlpi_error(q, DL_UNBIND_REQ, DL_OUTSTATE, 0);
+ dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0);
break;
}
us->sap = -1;
if (size < sizeof(dl_unitdata_req_t))
goto badprim;
if (us->state != DL_IDLE) {
- dlpi_error(q, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
+ dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
break;
}
if ((ppa = us->ppa) == 0) {
mp->b_rptr[1] = PPP_UI;
mp->b_rptr[2] = us->sap >> 8;
mp->b_rptr[3] = us->sap;
- if (!pass_packet(ppa, mp, 1)) {
- freemsg(mp);
- } else {
+ if (pass_packet(us, mp, 1)) {
if (!send_data(mp, us))
putq(q, mp);
}
#endif
case DL_CONNECT_REQ:
case DL_TOKEN_REQ:
- dlpi_error(q, d->dl_primitive, DL_NOTSUPPORTED, 0);
+ dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0);
break;
case DL_CONNECT_RES:
case DL_DISCONNECT_REQ:
case DL_RESET_REQ:
case DL_RESET_RES:
- dlpi_error(q, d->dl_primitive, DL_OUTSTATE, 0);
+ dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0);
break;
case DL_UDQOS_REQ:
- dlpi_error(q, d->dl_primitive, DL_BADQOSTYPE, 0);
+ dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0);
break;
#if DL_CURRENT_VERSION >= 2
cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
/* fall through */
badprim:
- dlpi_error(q, d->dl_primitive, DL_BADPRIM, 0);
+ dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
break;
}
freemsg(mp);
}
static void
-dlpi_error(q, prim, err, uerr)
+dlpi_error(q, us, prim, err, uerr)
queue_t *q;
+ upperstr_t *us;
int prim, err, uerr;
{
mblk_t *reply;
dl_error_ack_t *errp;
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err);
reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
if (reply == 0)
return; /* XXX should do bufcall */
#endif /* NO_DLPI */
static int
-pass_packet(ppa, mp, outbound)
- upperstr_t *ppa;
+pass_packet(us, mp, outbound)
+ upperstr_t *us;
mblk_t *mp;
int outbound;
{
+ int pass;
+ upperstr_t *ppa;
+
+ if (us->ppa == 0) {
+ freemsg(mp);
+ return 0;
+ }
+
+#ifdef FILTER_PACKETS
+ pass = ip_hard_filter(us, mp, outbound);
+#else
/*
* Here is where we might, in future, decide whether to pass
* or drop the packet, and whether it counts as link activity.
*/
- if (outbound)
- ppa->last_sent = time;
- else
- ppa->last_recv = time;
- return 1;
+ pass = 1;
+#endif /* FILTER_PACKETS */
+
+ if (pass < 0) {
+ /* pass only if link already up, and don't update time */
+ if (us->ppa->lowerq == 0) {
+ freemsg(mp);
+ return 0;
+ }
+ pass = 1;
+ } else if (pass) {
+ if (outbound)
+ ppa->last_sent = time;
+ else
+ ppa->last_recv = time;
+ }
+
+ return pass;
}
static int
us->flags |= US_CONTROL;
us->npmode = NPMODE_PASS;
- us->mtu = PPP_MRU;
+ us->mtu = PPP_MTU;
us->mru = PPP_MRU;
#ifdef SOL2
#ifdef INCR_IPACKETS
INCR_IPACKETS(ppa);
#endif
- if (!pass_packet(ppa, mp, 0)) {
- freemsg(mp);
- break;
- }
proto = PPP_PROTOCOL(mp->b_rptr);
if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
/*
* A data packet for some network protocol.
* Queue it on the upper stream for that protocol.
*/
+ if (!pass_packet(us, mp, 0))
+ break;
if (canput(us->q))
putq(us->q, mp);
else
}
}
}
+
+#ifdef FILTER_PACKETS
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+
+#define MAX_IPHDR 128 /* max TCP/IP header size */
+
+
+/* The following table contains a hard-coded list of protocol/port pairs.
+ * Any matching packets are either discarded unconditionally, or,
+ * if ok_if_link_up is non-zero when a connection does not currently exist
+ * (i.e., they go through if the connection is present, but never initiate
+ * a dial-out).
+ * This idea came from a post by dm@garage.uun.org (David Mazieres)
+ */
+static struct pktfilt_tab {
+ int proto;
+ u_short port;
+ u_short ok_if_link_up;
+} pktfilt_tab[] = {
+ { IPPROTO_UDP, 520, 1 }, /* RIP, ok to pass if link is up */
+ { IPPROTO_UDP, 123, 1 }, /* NTP, don't keep up the link for it */
+ { -1, 0, 0 } /* terminator entry has port == -1 */
+};
+
+
+static int
+ip_hard_filter(us, mp, outbound)
+ upperstr_t *us;
+ mblk_t *mp;
+ int outbound;
+{
+ struct ip *ip;
+ struct pktfilt_tab *pft;
+ mblk_t *temp_mp;
+ int proto;
+ int len, hlen;
+
+
+ /* Note, the PPP header has already been pulled up in all cases */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound);
+
+ switch (proto)
+ {
+ case PPP_IP:
+ if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN) {
+ temp_mp = mp->b_cont;
+ len = msgdsize(temp_mp);
+ hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR;
+ PULLUP(temp_mp, hlen);
+ if (temp_mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n",
+ us->mn, hlen);
+ mp->b_cont = 0; /* PULLUP() freed the rest */
+ freemsg(mp);
+ return 0;
+ }
+ ip = (struct ip *)mp->b_cont->b_rptr;
+ }
+ else {
+ len = msgdsize(mp);
+ hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR);
+ PULLUP(mp, hlen);
+ if (mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup failed, len=%d\n",
+ us->mn, hlen);
+ return 0;
+ }
+ ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN);
+ }
+
+ /* For IP traffic, certain packets (e.g., RIP) may be either
+ * 1. ignored - dropped completely
+ * 2. will not initiate a connection, but
+ * will be passed if a connection is currently up.
+ */
+ for (pft=pktfilt_tab; pft->proto != -1; pft++) {
+ if (ip->ip_p == pft->proto) {
+ switch(pft->proto) {
+ case IPPROTO_UDP:
+ if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ case IPPROTO_TCP:
+ if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ }
+ }
+ }
+ endfor:
+ if (pft->proto != -1) {
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n",
+ us->mn, pft->proto, pft->port);
+ /* Discard if not connected, or if not pass_with_link_up */
+ /* else, if link is up let go by, but don't update time */
+ return pft->ok_if_link_up? -1: 0;
+ }
+ break;
+ } /* end switch (proto) */
+
+ return 1;
+}
+#endif /* FILTER_PACKETS */
+