]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ipfilter/solaris.c
This commit was generated by cvs2svn to compensate for changes in r53568,
[FreeBSD/FreeBSD.git] / contrib / ipfilter / solaris.c
1 /*
2  * Copyright (C) 1993-1998 by Darren Reed.
3  *
4  * Redistribution and use in source and binary forms are permitted
5  * provided that this notice is preserved and due credit is given
6  * to the original author and the contributors.
7  */
8 /* #pragma ident   "@(#)solaris.c       1.12 6/5/96 (C) 1995 Darren Reed"*/
9 #pragma ident "@(#)$Id: solaris.c,v 2.1.2.5 1999/10/15 13:49:44 darrenr Exp $";
10
11 #include <sys/systm.h>
12 #include <sys/types.h>
13 #include <sys/param.h>
14 #include <sys/errno.h>
15 #include <sys/uio.h>
16 #include <sys/buf.h>
17 #include <sys/modctl.h>
18 #include <sys/open.h>
19 #include <sys/kmem.h>
20 #include <sys/conf.h>
21 #include <sys/cmn_err.h>
22 #include <sys/stat.h>
23 #include <sys/cred.h>
24 #include <sys/dditypes.h>
25 #include <sys/stream.h>
26 #include <sys/poll.h>
27 #include <sys/autoconf.h>
28 #include <sys/byteorder.h>
29 #include <sys/socket.h>
30 #include <sys/dlpi.h>
31 #include <sys/stropts.h>
32 #include <sys/sockio.h>
33 #include <net/if.h>
34 #include <net/af.h>
35 #include <net/route.h>
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/if_ether.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip_var.h>
41 #include <netinet/tcp.h>
42 #include <netinet/udp.h>
43 #include <netinet/tcpip.h>
44 #include <netinet/ip_icmp.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include "ip_compat.h"
48 #include "ipl.h"
49 #include "ip_fil.h"
50 #include "ip_nat.h"
51
52
53 char    _depends_on[] = "drv/ip";
54
55
56 void    solipdrvattach __P((void));
57 int     solipdrvdetach __P((void));
58
59 void    solattach __P((void));
60 int     soldetach __P((void));
61
62 extern  struct  filterstats     frstats[];
63 extern  KRWLOCK_T       ipf_mutex, ipfs_mutex, ipf_nat, ipf_solaris;
64 extern  kmutex_t        ipf_rw;
65 extern  int     fr_running;
66 extern  int     fr_flags;
67
68 extern ipnat_t *nat_list;
69
70 static  qif_t   *qif_head = NULL;
71 static  int     ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t,
72                                  void *, void **));
73 static  int     ipf_probe __P((dev_info_t *));
74 static  int     ipf_identify __P((dev_info_t *));
75 static  int     ipf_attach __P((dev_info_t *, ddi_attach_cmd_t));
76 static  int     ipf_detach __P((dev_info_t *, ddi_detach_cmd_t));
77 static  qif_t   *qif_from_queue __P((queue_t *));
78 static  void    fr_donotip __P((int, qif_t *, queue_t *, mblk_t *,
79                                 mblk_t *, ip_t *, size_t));
80 static  char    *ipf_devfiles[] = { IPL_NAME, IPL_NAT, IPL_STATE, IPL_AUTH,
81                                     NULL };
82 static  int     (*ipf_ip_inp) __P((queue_t *, mblk_t *)) = NULL;
83
84
85 #if SOLARIS2 >= 7
86 extern  void    ipfr_slowtimer __P((void *));
87 timeout_id_t    ipfr_timer_id;
88 static  timeout_id_t    synctimeoutid = 0;
89 #else
90 extern  void    ipfr_slowtimer __P((void));
91 int     ipfr_timer_id;
92 static  int     synctimeoutid = 0;
93 #endif
94
95 #ifdef  IPFDEBUG
96 void    printire __P((ire_t *));
97 #endif
98 static int      fr_precheck __P((mblk_t **, queue_t *, qif_t *, int));
99
100
101 static struct cb_ops ipf_cb_ops = {
102         iplopen,
103         iplclose,
104         nodev,          /* strategy */
105         nodev,          /* print */
106         nodev,          /* dump */
107         iplread,
108         nodev,          /* write */
109         iplioctl,       /* ioctl */
110         nodev,          /* devmap */
111         nodev,          /* mmap */
112         nodev,          /* segmap */
113         nochpoll,       /* poll */
114         ddi_prop_op,
115         NULL,
116         D_MTSAFE,
117 #if SOLARIS2 > 4
118         CB_REV,
119         nodev,          /* aread */
120         nodev,          /* awrite */
121 #endif
122 };
123
124 static struct dev_ops ipf_ops = {
125         DEVO_REV,
126         0,
127         ipf_getinfo,
128         ipf_identify,
129         ipf_probe,
130         ipf_attach,
131         ipf_detach,
132         nodev,          /* reset */
133         &ipf_cb_ops,
134         (struct bus_ops *)0
135 };
136
137 extern struct mod_ops mod_driverops;
138 static struct modldrv iplmod = {
139         &mod_driverops, IPL_VERSION, &ipf_ops };
140 static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL };
141
142
143 static dev_info_t *ipf_dev_info = NULL;
144
145
146 int _init()
147 {
148         int ipfinst;
149
150         if (fr_running < 0)
151                 return -1;
152         ipfinst = mod_install(&modlink1);
153 #ifdef  IPFDEBUG
154         cmn_err(CE_NOTE, "IP Filter: _init() = %d\n", ipfinst);
155 #endif
156         return ipfinst;
157 }
158
159
160 int _fini(void)
161 {
162         int ipfinst;
163
164         if (fr_running < 0)
165                 return -1;
166         ipfinst = mod_remove(&modlink1);
167 #ifdef  IPFDEBUG
168         cmn_err(CE_NOTE, "IP Filter: _fini() = %d\n", ipfinst);
169 #endif
170         return ipfinst;
171 }
172
173
174 int _info(modinfop)
175 struct modinfo *modinfop;
176 {
177         int ipfinst;
178
179         if (fr_running < 0)
180                 return -1;
181         ipfinst = mod_info(&modlink1, modinfop);
182 #ifdef  IPFDEBUG
183         cmn_err(CE_NOTE, "IP Filter: _info(%x) = %x\n", modinfop, ipfinst);
184 #endif
185         if (fr_running > 0)
186                 ipfsync();
187         return ipfinst;
188 }
189
190
191 static int ipf_probe(dip)
192 dev_info_t *dip;
193 {
194         if (fr_running < 0)
195                 return DDI_PROBE_FAILURE;
196 #ifdef  IPFDEBUG
197         cmn_err(CE_NOTE, "IP Filter: ipf_probe(%x)", dip);
198 #endif
199         return DDI_PROBE_SUCCESS;
200 }
201
202
203 static int ipf_identify(dip)
204 dev_info_t *dip;
205 {
206 #ifdef  IPFDEBUG
207         cmn_err(CE_NOTE, "IP Filter: ipf_identify(%x)", dip);
208 #endif
209         if (strcmp(ddi_get_name(dip), "ipf") == 0)
210                 return (DDI_IDENTIFIED);
211         return (DDI_NOT_IDENTIFIED);
212 }
213
214
215 static int ipf_attach(dip, cmd)
216 dev_info_t *dip;
217 ddi_attach_cmd_t cmd;
218 {
219 #ifdef  IPFDEBUG
220         int instance;
221
222         cmn_err(CE_NOTE, "IP Filter: ipf_attach(%x,%x)", dip, cmd);
223 #endif
224         switch (cmd) {
225         case DDI_ATTACH:
226                 if (fr_running < 0)
227                         break;
228 #ifdef  IPFDEBUG
229                 instance = ddi_get_instance(dip);
230
231                 cmn_err(CE_NOTE, "IP Filter: attach ipf instance %d", instance);
232 #endif
233                 if (ddi_create_minor_node(dip, "ipf", S_IFCHR, IPL_LOGIPF,
234                                           DDI_PSEUDO, 0) == DDI_FAILURE) {
235                         ddi_remove_minor_node(dip, NULL);
236                         goto attach_failed;
237                 }
238                 if (ddi_create_minor_node(dip, "ipnat", S_IFCHR, IPL_LOGNAT,
239                                           DDI_PSEUDO, 0) == DDI_FAILURE) {
240                         ddi_remove_minor_node(dip, NULL);
241                         goto attach_failed;
242                 }
243                 if (ddi_create_minor_node(dip, "ipstate", S_IFCHR,IPL_LOGSTATE,
244                                           DDI_PSEUDO, 0) == DDI_FAILURE) {
245                         ddi_remove_minor_node(dip, NULL);
246                         goto attach_failed;
247                 }
248                 if (ddi_create_minor_node(dip, "ipauth", S_IFCHR, IPL_LOGAUTH,
249                                           DDI_PSEUDO, 0) == DDI_FAILURE) {
250                         ddi_remove_minor_node(dip, NULL);
251                         goto attach_failed;
252                 }
253                 ipf_dev_info = dip;
254                 sync();
255                 /*
256                  * Initialize mutex's
257                  */
258                 if (iplattach() == -1)
259                         goto attach_failed;
260                 /*
261                  * Lock people out while we set things up.
262                  */
263                 WRITE_ENTER(&ipf_solaris);
264                 solattach();
265                 solipdrvattach();
266                 RWLOCK_EXIT(&ipf_solaris);
267                 cmn_err(CE_CONT, "%s, attaching complete.\n", ipfilter_version);
268                 sync();
269                 if (fr_running == 0)
270                         fr_running = 1;
271                 if (ipfr_timer_id == 0)
272                         ipfr_timer_id = timeout(ipfr_slowtimer, NULL,
273                                                 drv_usectohz(500000));
274                 if (fr_running == 1)
275                         return DDI_SUCCESS;
276         default:
277                 return DDI_FAILURE;
278         }
279
280 attach_failed:
281         cmn_err(CE_NOTE, "IP Filter: failed to attach\n");
282         /*
283          * Use our own detach routine to toss
284          * away any stuff we allocated above.
285          */
286         (void) ipf_detach(dip, DDI_DETACH);
287         return DDI_FAILURE;
288 }
289
290
291 static int ipf_detach(dip, cmd)
292 dev_info_t *dip;
293 ddi_detach_cmd_t cmd;
294 {
295         int i;
296
297 #ifdef  IPFDEBUG
298         cmn_err(CE_NOTE, "IP Filter: ipf_detach(%x,%x)", dip, cmd);
299 #endif
300         switch (cmd) {
301         case DDI_DETACH:
302                 if (fr_running <= 0)
303                         break;
304                 /*
305                  * Make sure we're the only one's modifying things.  With
306                  * this lock others should just fall out of the loop.
307                  */
308                 mutex_enter(&ipf_rw);
309                 if (ipfr_timer_id != 0) {
310                         untimeout(ipfr_timer_id);
311                         ipfr_timer_id = 0;
312                 }
313                 mutex_exit(&ipf_rw);
314                 WRITE_ENTER(&ipf_solaris);
315                 mutex_enter(&ipf_rw);
316                 if (fr_running <= 0) {
317                         mutex_exit(&ipf_rw);
318                         return DDI_FAILURE;
319                 }
320                 fr_running = -1;
321                 mutex_exit(&ipf_rw);
322                 /* NOTE: ipf_solaris rwlock is released in ipldetach */
323
324                 /*
325                  * Undo what we did in ipf_attach, freeing resources
326                  * and removing things we installed.  The system
327                  * framework guarantees we are not active with this devinfo
328                  * node in any other entry points at this time.
329                  */
330                 ddi_prop_remove_all(dip);
331                 i = ddi_get_instance(dip);
332                 ddi_remove_minor_node(dip, NULL);
333                 sync();
334                 i = solipdrvdetach();
335                 if (i > 0) {
336                         cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i);
337                         return DDI_FAILURE;
338                 }
339                 if (!soldetach()) {
340                         cmn_err(CE_CONT, "IP Filter: detached\n");
341                         return (DDI_SUCCESS);
342                 }
343         default:
344                 return (DDI_FAILURE);
345         }
346         return DDI_FAILURE;
347 }
348
349
350 static int ipf_getinfo(dip, infocmd, arg, result)
351 dev_info_t *dip;
352 ddi_info_cmd_t infocmd;
353 void *arg, **result;
354 {
355         int error;
356
357         if (fr_running <= 0)
358                 return DDI_FAILURE;
359         error = DDI_FAILURE;
360 #ifdef  IPFDEBUG
361         cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%x,%x,%x)", dip, infocmd, arg);
362 #endif
363         switch (infocmd) {
364         case DDI_INFO_DEVT2DEVINFO:
365                 *result = ipf_dev_info;
366                 error = DDI_SUCCESS;
367                 break;
368         case DDI_INFO_DEVT2INSTANCE:
369                 *result = (void *)getminor((dev_t) arg);
370                 error = DDI_SUCCESS;
371                 break;
372         default:
373                 break;
374         }
375         return (error);
376 }
377
378 /*
379  * find the filter structure setup for this queue
380  */
381 static qif_t *qif_from_queue(q)
382 queue_t *q;
383 {
384         qif_t *qif;
385
386         for (qif = qif_head; qif; qif = qif->qf_next)
387                 if ((qif->qf_iptr == q->q_ptr) || (qif->qf_optr == q->q_ptr))
388                         break;
389         return qif;
390 }
391
392
393 /*
394  * OK, this is pretty scrappy code, but then it's essentially just here for
395  * debug purposes and that's it.  Packets should not normally come through
396  * here, and if they do, well, we would like to see as much information as
397  * possible about them and what they claim to hold.
398  */
399 void fr_donotip(out, qif, q, m, mt, ip, off)
400 int out;
401 qif_t *qif;
402 queue_t *q;
403 mblk_t *m, *mt;
404 ip_t *ip;
405 size_t off;
406 {
407         u_char *s, outb[256], *t;
408         int i;
409
410         outb[0] = '\0';
411         outb[1] = '\0';
412         outb[2] = '\0';
413         outb[3] = '\0';
414         s = ip ? (u_char *)ip : outb;
415         if (!ip && (m == mt) && m->b_cont && (MTYPE(m) != M_DATA))
416                 m = m->b_cont;
417
418         printf("!IP %s:%d %d %p %p %p %d %p/%d %p/%d %p %d %d %p\n",
419                 qif ? qif->qf_name : "?", out, qif->qf_hl, q,
420                 q ? q->q_ptr : NULL, q ? q->q_qinfo : NULL,
421                 mt->b_wptr - mt->b_rptr, m, MTYPE(m), mt, MTYPE(mt), m->b_rptr,
422                 m->b_wptr - m->b_rptr, off, ip);
423         printf("%02x%02x%02x%02x\n", *s, *(s+1), *(s+2), *(s+3));
424         while (m != mt) {
425                 i = 0;
426                 t = outb;
427                 s = mt->b_rptr;
428                 sprintf((char *)t, "%d:", MTYPE(mt));
429                 t += strlen((char *)t);
430                 for (; (i < 100) && (s < mt->b_wptr); i++) {
431                         sprintf((char *)t, "%02x%s", *s++,
432                                 ((i & 3) == 3) ? " " : "");
433                         t += ((i & 3) == 3) ? 3 : 2;
434                 }
435                 *t++ = '\n';
436                 *t = '\0';
437                 printf("%s", outb);
438                 mt = mt->b_cont;
439         }
440         i = 0;
441         t = outb;
442         s = m->b_rptr;
443         sprintf((char *)t, "%d:", MTYPE(m));
444         t += strlen((char *)t);
445         for (; (i < 100) && (s < m->b_wptr); i++) {
446                 sprintf((char *)t, "%02x%s", *s++, ((i & 3) == 3) ? " " : "");
447                 t += ((i & 3) == 3) ? 3 : 2;
448         }
449         *t++ = '\n';
450         *t = '\0';
451         printf("%s", outb);
452 }
453
454
455 /*
456  * find the first data mblk, if present, in the chain we're processing.  Also
457  * make a few sanity checks to try prevent the filter from causing a panic -
458  * none of the nice IP sanity checks (including checksumming) should have been
459  * done yet (for incoming packets) - dangerous!
460  */
461 static int fr_precheck(mp, q, qif, out)
462 mblk_t **mp;
463 queue_t *q;
464 qif_t *qif;
465 int out;
466 {
467         register mblk_t *m, *mt = *mp;
468         register ip_t *ip;
469         size_t hlen, len, off, mlen, iphlen;
470         int err, synced = 0;
471         u_char *bp;
472 #ifndef sparc
473         u_short __iplen, __ipoff;
474 #endif
475 tryagain:
476         /*
477          * If there is only M_DATA for a packet going out, then any header
478          * information (which would otherwise appear in an M_PROTO mblk before
479          * the M_DATA) is prepended before the IP header.  We need to set the
480          * offset to account for this. - see MMM
481          */
482         off = (out) ? qif->qf_hl : 0;
483
484         /*
485          * If the message protocol block indicates that there isn't a data
486          * block following it, just return back.
487          */
488         bp = (u_char *)ALIGN32(mt->b_rptr);
489         if (MTYPE(mt) == M_PROTO || MTYPE(mt) == M_PCPROTO) {
490                 dl_unitdata_ind_t *dl = (dl_unitdata_ind_t *)bp;
491                 if (dl->dl_primitive != DL_UNITDATA_IND &&
492                     dl->dl_primitive != DL_UNITDATA_REQ) {
493                         frstats[out].fr_notdata++;
494                         return 0;
495                 }
496         }
497
498         /*
499          * Find the first data block, count the data blocks in this chain and
500          * the total amount of data.
501          */
502         for (m = mt; m && (MTYPE(m) != M_DATA); m = m->b_cont)
503                 off = 0;        /* Any non-M_DATA cancels the offset */
504
505         if (!m) {
506                 frstats[out].fr_nodata++;
507                 return 0;       /* No data blocks */
508         }
509
510         /*
511          * This is a complete kludge to try and work around some bizarre
512          * packets which drop through into fr_donotip.
513          */
514         if ((mt != m) && (MTYPE(mt) == M_PROTO || MTYPE(mt) == M_PCPROTO)) {
515                 dl_unitdata_ind_t *dl = (dl_unitdata_ind_t *)bp;
516                 if ((dl->dl_primitive == DL_UNITDATA_IND) &&
517                     (dl->dl_group_address == 1))
518                         if (((*((u_char *)m->b_rptr) == 0x0) &&
519                             ((*((u_char *)m->b_rptr + 2) == 0x45))))
520                                 off += 2;
521         }
522
523         ip = (ip_t *)(m->b_rptr + off);         /* MMM */
524
525         /*
526          * We might have a 1st data block which is really M_PROTO, i.e. it is
527          * only big enough for the link layer header
528          */
529         while ((u_char *)ip >= m->b_wptr) {
530                 len = (u_char *)ip - m->b_wptr;
531                 m = m->b_cont;
532                 if (m == NULL)
533                         return 0;       /* not enough data for IP */
534                 ip = (ip_t *)(m->b_rptr + len);
535         }
536         off = (u_char *)ip - m->b_rptr;
537         if (off != 0)
538                 m->b_rptr = (u_char *)ip;
539         mlen = msgdsize(m);
540
541         len = m->b_wptr - m->b_rptr;
542         if (m->b_wptr < m->b_rptr) {
543                 cmn_err(CE_NOTE, "IP Filter: Bad packet: wptr %p < rptr %p",
544                         m->b_wptr, m->b_rptr);
545                 frstats[out].fr_bad++;
546                 return -1;
547         }
548         /*
549          * Ok, the IP header isn't on a 32bit aligned address so junk it.
550          */
551         if (((u_int)ip & 0x3) || (len < sizeof(*ip))) {
552                 /*
553                  * We have link layer header and IP header in the same mbuf,
554                  * problem being that a pullup without adjusting b_rptr will
555                  * bring us back here again as it's likely that the start of
556                  * the databuffer (b_datab->db_base) is already aligned.  Hmm,
557                  * should we pull it all up (length of -1 to pullupmsg) if we
558                  * can, now ?
559                  */
560 fixalign:
561                 if (off == (u_char *)ip - m->b_rptr) {
562                         m->b_rptr += off;
563                         off = 0;
564                 }
565                 if (!pullupmsg(m, sizeof(ip_t) + off)) {
566                         cmn_err(CE_NOTE, "pullupmsg failed\n");
567                         frstats[out].fr_pull[1]++;
568                         return -1;
569                 }
570                 frstats[out].fr_pull[0]++;
571                 synced = 1;
572                 off = 0;
573                 goto tryagain;
574
575         }
576         if (ip->ip_v != IPVERSION) {
577                 m->b_rptr -= off;
578                 if (!synced) {
579                         synced = 1;
580                         RWLOCK_EXIT(&ipfs_mutex);
581                         ipfsync();
582                         READ_ENTER(&ipfs_mutex);
583                         goto tryagain;
584                 }
585                 fr_donotip(out, qif, q, m, mt, ip, off);
586                 frstats[out].fr_notip++;
587                 return (fr_flags & FF_BLOCKNONIP) ? -1 : 0;
588         }
589
590 #ifndef sparc
591         __iplen = (u_short)ip->ip_len,
592         __ipoff = (u_short)ip->ip_off;
593
594         ip->ip_len = ntohs(__iplen);
595         ip->ip_off = ntohs(__ipoff);
596 #endif
597
598         hlen = iphlen = ip->ip_hl << 2;
599
600         if ((iphlen < sizeof(ip_t)) || (iphlen > (u_short)ip->ip_len) ||
601             (mlen < (u_short)ip->ip_len)) {
602                 /*
603                  * Bad IP packet or not enough data/data length mismatches
604                  */
605                 cmn_err(CE_NOTE,
606                         "IP Filter: Bad packet: iphlen %u ip_len %u mlen %u",
607                         iphlen, ip->ip_len, mlen);
608 #ifndef sparc
609                 __iplen = (u_short)ip->ip_len,
610                 __ipoff = (u_short)ip->ip_off;
611
612                 ip->ip_len = htons(__iplen);
613                 ip->ip_off = htons(__ipoff);
614 #endif
615                 m->b_rptr -= off;
616                 frstats[out].fr_bad++;
617                 return -1;
618         }
619
620         /*
621          * Make hlen the total size of the IP header plus TCP/UDP/ICMP header
622          * (if it is one of these three).
623          */
624         if ((ip->ip_off & IP_OFFMASK) == 0)
625                 switch (ip->ip_p)
626                 {
627                 case IPPROTO_TCP :
628                         hlen += sizeof(tcphdr_t);
629                         break;
630                 case IPPROTO_UDP :
631                         hlen += sizeof(udphdr_t);
632                         break;
633                 case IPPROTO_ICMP :
634                         /* 76 bytes is enough for a complete ICMP error. */
635                         hlen += 76 + sizeof(icmphdr_t);
636                         break;
637                 default :
638                         break;
639                 }
640
641         if (hlen > mlen)
642                 hlen = mlen;
643
644         /*
645          * If we don't have enough data in the mblk or we haven't yet copied
646          * enough (above), then copy some more.
647          */
648         if ((hlen > len)) {
649                 if (!pullupmsg(m, (int)hlen)) {
650                         cmn_err(CE_NOTE, "pullupmsg failed\n");
651                         frstats[out].fr_pull[1]++;
652                         return -1;
653                 }
654                 frstats[out].fr_pull[0]++;
655                 ip = (ip_t *)ALIGN32(m->b_rptr);
656         }
657         qif->qf_m = m;
658         qif->qf_q = q;
659         qif->qf_off = off;
660         qif->qf_len = len;
661         err = fr_check(ip, iphlen, qif->qf_ill, out, qif, mp);
662         if (err == 2)
663                 goto fixalign;
664         /*
665          * Copy back the ip header data if it was changed, we haven't yet
666          * freed the message and we aren't going to drop the packet.
667          * BUT only do this if there were no changes to the buffer, else
668          * we can't be sure that the ip pointer is still correct!
669          */
670         if (*mp != NULL) {
671                 if (*mp == mt) {
672                         m->b_rptr -= off;
673 #ifndef sparc
674                         __iplen = (u_short)ip->ip_len,
675                         __ipoff = (u_short)ip->ip_off;
676
677                         ip->ip_len = htons(__iplen);
678                         ip->ip_off = htons(__ipoff);
679 #endif
680                 } else
681                         cmn_err(CE_NOTE,
682                                 "IP Filter: *mp %p mt %p %s\n", *mp, mt,
683                                 "mblk changed, cannot revert ip_len, ip_off");
684         }
685         return err;
686 }
687
688
689 int fr_qin(q, mb)
690 queue_t *q;
691 mblk_t *mb;
692 {
693         int (*pnext) __P((queue_t *, mblk_t *)), type, synced = 0, err = 0;
694         qif_t qf, *qif;
695
696         if (fr_running <= 0) {
697                 mb->b_prev = NULL;
698                 freemsg(mb);
699                 return 0;
700         }
701
702         READ_ENTER(&ipf_solaris);
703 again:
704         if (fr_running <= 0) {
705                 RWLOCK_EXIT(&ipf_solaris);
706                 mb->b_prev = NULL;
707                 freemsg(mb);
708                 return 0;
709         }
710         READ_ENTER(&ipfs_mutex);
711         if (!(qif = qif_from_queue(q))) {
712                 for (qif = qif_head; qif; qif = qif->qf_next)
713                         if (&qif->qf_rqinit == q->q_qinfo && qif->qf_rqinfo &&
714                                         qif->qf_rqinfo->qi_putp) {
715                                 pnext = qif->qf_rqinfo->qi_putp;
716                                 frstats[0].fr_notip++;
717                                 RWLOCK_EXIT(&ipfs_mutex);
718                                 if (!synced) {
719                                         ipfsync();
720                                         synced = 1;
721                                         goto again;
722                                 }
723                                 RWLOCK_EXIT(&ipf_solaris);
724                                 /* fr_donotip(0, NULL, q, mb, mb, NULL, 0); */
725                                 return (*pnext)(q, mb);
726                         }
727                 RWLOCK_EXIT(&ipfs_mutex);
728                 if (!synced) {
729                         ipfsync();
730                         synced = 1;
731                         goto again;
732                 }
733                 cmn_err(CE_WARN,
734                         "IP Filter: dropped: fr_qin(%x,%x): type %x qif %x",
735                         q, mb, MTYPE(mb), qif);
736                 cmn_err(CE_CONT,
737                         "IP Filter: info %x next %x ptr %x fsrv %x bsrv %x\n",
738                         q->q_qinfo, q->q_next, q->q_ptr, q->q_nfsrv,
739                         q->q_nbsrv);
740                 cmn_err(CE_CONT, "IP Filter: info: putp %x srvp %x info %x\n",
741                         q->q_qinfo->qi_putp, q->q_qinfo->qi_srvp,
742 #if SOLARIS > 3
743                         q->q_qinfo->qi_infop
744 #else
745                         0
746 #endif
747                         );
748                 frstats[0].fr_drop++;
749                 RWLOCK_EXIT(&ipf_solaris);
750                 mb->b_prev = NULL;
751                 freemsg(mb);
752                 return 0;
753         }
754
755         bcopy((char *)qif, (char *)&qf, sizeof(qf));
756         qif = &qf;
757         type = MTYPE(mb);
758         pnext = qif->qf_rqinfo->qi_putp;
759
760         if (datamsg(type) || (type == M_BREAK))
761                 err = fr_precheck(&mb, q, qif, 0);
762
763         RWLOCK_EXIT(&ipfs_mutex);
764         RWLOCK_EXIT(&ipf_solaris);
765
766         if ((err == 0) && (mb != NULL)) {
767                 if (pnext)
768                         return (*pnext)(q, mb);
769
770                 cmn_err(CE_WARN, "IP Filter: inp NULL: qif %x q %x info %x",
771                         qif, q, q->q_qinfo);
772         }
773         if (mb) {
774                 mb->b_prev = NULL;
775                 freemsg(mb);
776         }
777         return 0;
778 }
779
780
781 int fr_qout(q, mb)
782 queue_t *q;
783 mblk_t *mb;
784 {
785         int (*pnext) __P((queue_t *, mblk_t *)), type, synced = 0, err = 0;
786         qif_t qf, *qif;
787
788         if (fr_running <= 0) {
789                 mb->b_prev = NULL;
790                 freemsg(mb);
791                 return 0;
792         }
793
794         READ_ENTER(&ipf_solaris);
795 again:
796         if (fr_running <= 0) {
797                 RWLOCK_EXIT(&ipf_solaris);
798                 mb->b_prev = NULL;
799                 freemsg(mb);
800                 return 0;
801         }
802         READ_ENTER(&ipfs_mutex);
803         if (!(qif = qif_from_queue(q))) {
804                 for (qif = qif_head; qif; qif = qif->qf_next)
805                         if (&qif->qf_wqinit == q->q_qinfo && qif->qf_wqinfo &&
806                                         qif->qf_wqinfo->qi_putp) {
807                                 pnext = qif->qf_wqinfo->qi_putp;
808                                 RWLOCK_EXIT(&ipfs_mutex);
809                                 frstats[1].fr_notip++;
810                                 if (!synced) {
811                                         ipfsync();
812                                         synced = 1;
813                                         goto again;
814                                 }
815                                 /* fr_donotip(0, NULL, q, mb, mb, NULL, 0); */
816                                 RWLOCK_EXIT(&ipf_solaris);
817                                 return (*pnext)(q, mb);
818                         }
819                 RWLOCK_EXIT(&ipfs_mutex);
820                 if (!synced) {
821                         ipfsync();
822                         synced = 1;
823                         goto again;
824                 }
825                 cmn_err(CE_WARN,
826                         "IP Filter: dropped: fr_qout(%x,%x): type %x: qif %x",
827                         q, mb, MTYPE(mb), qif);
828                 cmn_err(CE_CONT,
829                         "IP Filter: info %x next %x ptr %x fsrv %x bsrv %x\n",
830                         q->q_qinfo, q->q_next, q->q_ptr, q->q_nfsrv,
831                         q->q_nbsrv);
832                 cmn_err(CE_CONT, "IP Filter: info: putp %x srvp %x info %x\n",
833                         q->q_qinfo->qi_putp, q->q_qinfo->qi_srvp,
834 #if SOLARIS > 3
835                         q->q_qinfo->qi_infop
836 #else
837                         0
838 #endif
839                         );
840                 if (q->q_nfsrv)
841                         cmn_err(CE_CONT,
842                                 "IP Filter: nfsrv: info %x next %x ptr %x\n",
843                                 q->q_nfsrv->q_qinfo, q->q_nfsrv->q_next,
844                                 q->q_nfsrv->q_ptr);
845                 if (q->q_nbsrv)
846                         cmn_err(CE_CONT,
847                                 "IP Filter: nbsrv: info %x next %x ptr %x\n",
848                                 q->q_nbsrv->q_qinfo, q->q_nbsrv->q_next,
849                                 q->q_nbsrv->q_ptr);
850                 frstats[1].fr_drop++;
851                 RWLOCK_EXIT(&ipf_solaris);
852                 mb->b_prev = NULL;
853                 freemsg(mb);
854                 return 0;
855         }
856
857         bcopy((char *)qif, (char *)&qf, sizeof(qf));
858         qif = &qf;
859         type = MTYPE(mb);
860         pnext = qif->qf_wqinfo->qi_putp;
861
862         if (datamsg(type) || (type == M_BREAK))
863                 err = fr_precheck(&mb, q, qif, 1);
864
865         RWLOCK_EXIT(&ipfs_mutex);
866         RWLOCK_EXIT(&ipf_solaris);
867
868         if ((err == 0) && (mb != NULL)) {
869                 if (pnext)
870                         return (*pnext)(q, mb);
871
872                 cmn_err(CE_WARN, "IP Filter: outp NULL: qif %x %s q %x info %x",
873                         qif, qif->qf_name, q, q->q_qinfo);
874         }
875         if (mb) {
876                 mb->b_prev = NULL;
877                 freemsg(mb);
878         }
879         return 0;
880 }
881
882
883 void ipf_synctimeout(arg)
884 void *arg;
885 {
886         READ_ENTER(&ipf_solaris);
887         ipfsync();
888         WRITE_ENTER(&ipfs_mutex);
889         synctimeoutid = 0;
890         RWLOCK_EXIT(&ipfs_mutex);
891         RWLOCK_EXIT(&ipf_solaris);
892 }
893
894 static int ipf_ip_qin(q, mb)
895 queue_t *q;
896 mblk_t *mb;
897 {
898         struct iocblk *ioc;
899         int ret;
900
901         if (fr_running <= 0) {
902                 mb->b_prev = NULL;
903                 freemsg(mb);
904                 return 0;
905         }
906  
907         if (MTYPE(mb) != M_IOCTL)
908                 return (*ipf_ip_inp)(q, mb);
909
910         READ_ENTER(&ipf_solaris);
911         if (fr_running <= 0) {
912                 RWLOCK_EXIT(&ipf_solaris);
913                 mb->b_prev = NULL;
914                 freemsg(mb);
915                 return 0;
916         }
917         ioc = (struct iocblk *)mb->b_rptr;
918
919         switch (ioc->ioc_cmd) {
920         case I_LINK:
921         case I_UNLINK:
922         case SIOCSIFADDR:
923         case SIOCSIFFLAGS:
924 #ifdef  IPFDEBUG
925                 cmn_err(CE_NOTE, "IP Filter: ipf_ip_qin() M_IOCTL type=0x%x\n", ioc->ioc_cmd);
926 #endif
927                 ret = (*ipf_ip_inp)(q, mb);
928
929                 WRITE_ENTER(&ipfs_mutex);
930                 if (synctimeoutid == 0) {
931                         synctimeoutid = timeout(ipf_synctimeout,
932                                                 NULL,
933                                                 drv_usectohz(1000000) /*1 sec*/
934                                         );
935                 }
936
937                 RWLOCK_EXIT(&ipfs_mutex);
938                 break;
939         default:
940                 ret = (*ipf_ip_inp)(q, mb);
941         }
942         RWLOCK_EXIT(&ipf_solaris);
943         return ret;
944 }
945
946 static int ipdrvattcnt = 0;
947 extern struct streamtab ipinfo;
948
949 void solipdrvattach()
950 {
951 #ifdef  IPFDEBUG
952         cmn_err(CE_NOTE, "IP Filter: solipdrvattach() %d ipinfo=0x%lx\n",
953                 ipdrvattcnt, &ipinfo);
954 #endif
955
956         if (++ipdrvattcnt == 1) {
957                 if (ipf_ip_inp == NULL) {
958                         ipf_ip_inp = ipinfo.st_wrinit->qi_putp;
959                         ipinfo.st_wrinit->qi_putp = ipf_ip_qin;
960                 }
961         }
962 }
963
964 int solipdrvdetach()
965 {
966 #ifdef  IPFDEBUG
967         cmn_err(CE_NOTE, "IP Filter: solipdrvdetach() %d ipinfo=0x%lx\n",
968                 ipdrvattcnt, &ipinfo);
969 #endif
970
971         WRITE_ENTER(&ipfs_mutex);
972         if (--ipdrvattcnt <= 0) {
973                 if (ipf_ip_inp && (ipinfo.st_wrinit->qi_putp == ipf_ip_qin)) {
974                         ipinfo.st_wrinit->qi_putp = ipf_ip_inp;
975                         ipf_ip_inp = NULL;
976                 }
977                 if (synctimeoutid) {
978                         untimeout(synctimeoutid);
979                         synctimeoutid = 0;
980                 }
981         }
982         RWLOCK_EXIT(&ipfs_mutex);
983         return ipdrvattcnt;
984 }
985
986 /*
987  * attach the packet filter to each interface that is defined as having an
988  * IP address associated with it and save some of the info. for that struct
989  * so we're not out of date as soon as the ill disappears - but we must sync
990  * to be correct!
991  */
992 void solattach()
993 {
994         queue_t *in, *out;
995         struct frentry *f;
996         qif_t *qif, *qf2;
997         ipnat_t *np;
998         size_t len;
999         ill_t *il;
1000
1001         for (il = ill_g_head; il; il = il->ill_next) {
1002                 in = il->ill_rq;
1003                 if (!in || !il->ill_wq)
1004                         continue;
1005
1006                 out = il->ill_wq->q_next;
1007
1008                 WRITE_ENTER(&ipfs_mutex);
1009                 /*
1010                  * Look for entry already setup for this device
1011                  */
1012                 for (qif = qif_head; qif; qif = qif->qf_next)
1013                         if (qif->qf_iptr == in->q_ptr &&
1014                             qif->qf_optr == out->q_ptr)
1015                                 break;
1016                 if (qif) {
1017                         RWLOCK_EXIT(&ipfs_mutex);
1018                         continue;
1019                 }
1020 #ifdef  IPFDEBUG
1021                 cmn_err(CE_NOTE,
1022                         "IP Filter: il %x ipt %x opt %x ipu %x opu %x i %x/%x",
1023                         il, in->q_ptr,  out->q_ptr, in->q_qinfo->qi_putp,
1024                         out->q_qinfo->qi_putp, out->q_qinfo, in->q_qinfo);
1025 #endif
1026                 KMALLOC(qif, qif_t *);
1027                 if (!qif) {
1028                         cmn_err(CE_NOTE,
1029                                 "IP Filter: malloc(%d) for qif_t failed\n",
1030                                 sizeof(qif_t));
1031                         RWLOCK_EXIT(&ipfs_mutex);
1032                         continue;
1033                 }
1034
1035                 if (in->q_qinfo->qi_putp == fr_qin) {
1036                         for (qf2 = qif_head; qf2; qf2 = qf2->qf_next)
1037                                 if (&qf2->qf_rqinit == in->q_qinfo) {
1038                                         qif->qf_rqinfo = qf2->qf_rqinfo;
1039                                         break;
1040                                 }
1041                         if (!qf2) {
1042 #ifdef  IPFDEBUG
1043                                 cmn_err(CE_WARN,
1044                                         "IP Filter: rq:%s put %x qi %x",
1045                                         il->ill_name, in->q_qinfo->qi_putp,
1046                                         in->q_qinfo);
1047 #endif
1048                                 RWLOCK_EXIT(&ipfs_mutex);
1049                                 KFREE(qif);
1050                                 continue;
1051                         }
1052                 } else
1053                         qif->qf_rqinfo = in->q_qinfo;
1054
1055                 if (out->q_qinfo->qi_putp == fr_qout) {
1056                         for (qf2 = qif_head; qf2; qf2 = qf2->qf_next)
1057                                 if (&qf2->qf_wqinit == out->q_qinfo) {
1058                                         qif->qf_wqinfo = qf2->qf_wqinfo;
1059                                         break;
1060                                 }
1061                         if (!qf2) {
1062 #ifdef  IPFDEBUG
1063                                 cmn_err(CE_WARN,
1064                                         "IP Filter: wq:%s put %x qi %x",
1065                                         il->ill_name, out->q_qinfo->qi_putp,
1066                                         out->q_qinfo);
1067 #endif
1068                                 RWLOCK_EXIT(&ipfs_mutex);
1069                                 KFREE(qif);
1070                                 continue;
1071                         }
1072                 } else
1073                         qif->qf_wqinfo = out->q_qinfo;
1074
1075                 qif->qf_ill = il;
1076                 qif->qf_in = in;
1077                 qif->qf_out = out;
1078                 qif->qf_iptr = in->q_ptr;
1079                 qif->qf_optr = out->q_ptr;
1080                 qif->qf_hl = il->ill_hdr_length;
1081                 strncpy(qif->qf_name, il->ill_name, sizeof(qif->qf_name));
1082                 qif->qf_name[sizeof(qif->qf_name) - 1] = '\0';
1083
1084                 qif->qf_next = qif_head;
1085                 qif_head = qif;
1086
1087                 /*
1088                  * Activate any rules directly associated with this interface
1089                  */
1090                 WRITE_ENTER(&ipf_mutex);
1091                 for (f = ipfilter[0][fr_active]; f; f = f->fr_next) {
1092                         if ((f->fr_ifa == (struct ifnet *)-1)) {
1093                                 len = strlen(f->fr_ifname) + 1;
1094                                 if ((len != 0) &&
1095                                     (len == (size_t)il->ill_name_length) &&
1096                                     !strncmp(il->ill_name, f->fr_ifname, len))
1097                                         f->fr_ifa = il;
1098                         }
1099                 }
1100                 for (f = ipfilter[1][fr_active]; f; f = f->fr_next) {
1101                         if ((f->fr_ifa == (struct ifnet *)-1)) {
1102                                 len = strlen(f->fr_ifname) + 1;
1103                                 if ((len != 0) &&
1104                                     (len == (size_t)il->ill_name_length) &&
1105                                     !strncmp(il->ill_name, f->fr_ifname, len))
1106                                         f->fr_ifa = il;
1107                         }
1108                 }
1109                 RWLOCK_EXIT(&ipf_mutex);
1110                 WRITE_ENTER(&ipf_nat);
1111                 for (np = nat_list; np; np = np->in_next) {
1112                         if ((np->in_ifp == (struct ifnet *)-1)) {
1113                                 len = strlen(np->in_ifname) + 1;
1114                                 if ((len != 0) &&
1115                                     (len == (size_t)il->ill_name_length) &&
1116                                     !strncmp(il->ill_name, np->in_ifname, len))
1117                                         np->in_ifp = il;
1118                         }
1119                 }
1120                 RWLOCK_EXIT(&ipf_nat);
1121
1122                 bcopy((caddr_t)qif->qf_rqinfo, (caddr_t)&qif->qf_rqinit,
1123                       sizeof(struct qinit));
1124                 qif->qf_rqinit.qi_putp = fr_qin;
1125 #ifdef  IPFDEBUG
1126                 cmn_err(CE_NOTE,
1127                         "IP Filter: solattach: in queue(%lx)->q_qinfo FROM %lx TO %lx",
1128                         in, in->q_qinfo, &qif->qf_rqinit
1129                         );
1130 #endif
1131                 in->q_qinfo = &qif->qf_rqinit;
1132
1133                 bcopy((caddr_t)qif->qf_wqinfo, (caddr_t)&qif->qf_wqinit,
1134                       sizeof(struct qinit));
1135                 qif->qf_wqinit.qi_putp = fr_qout;
1136 #ifdef  IPFDEBUG
1137                 cmn_err(CE_NOTE,
1138                         "IP Filter: solattach: out queue(%lx)->q_qinfo FROM %lx TO %lx",
1139                         out, out->q_qinfo, &qif->qf_wqinit
1140                         );
1141 #endif
1142                 out->q_qinfo = &qif->qf_wqinit;
1143
1144                 RWLOCK_EXIT(&ipfs_mutex);
1145                 cmn_err(CE_CONT, "IP Filter: attach to [%s,%d]\n",
1146                         qif->qf_name, il->ill_ppa);
1147         }
1148         if (!qif_head)
1149                 cmn_err(CE_CONT, "IP Filter: not attached to any interfaces\n");
1150         return;
1151 }
1152
1153
1154 /*
1155  * look for bad consistancies between the list of interfaces the filter knows
1156  * about and those which are currently configured.
1157  */
1158 int ipfsync()
1159 {
1160         register struct frentry *f;
1161         register ipnat_t *np;
1162         register qif_t *qif, **qp;
1163         register ill_t *il;
1164         queue_t *in, *out;
1165
1166         WRITE_ENTER(&ipfs_mutex);
1167         for (qp = &qif_head; (qif = *qp); ) {
1168                 for (il = ill_g_head; il; il = il->ill_next)
1169                         if ((qif->qf_ill == il) &&
1170                             !strcmp(qif->qf_name, il->ill_name)) {
1171                                 mblk_t  *m = il->ill_hdr_mp;
1172
1173                                 qif->qf_hl = il->ill_hdr_length;
1174                                 if (m && qif->qf_hl != (m->b_wptr - m->b_rptr))
1175                                         cmn_err(CE_NOTE,
1176                                                 "IP Filter: ILL Header Length Mismatch\n");
1177                                 break;
1178                         }
1179                 if (il) {
1180                         qp = &qif->qf_next;
1181                         continue;
1182                 }
1183                 cmn_err(CE_CONT, "IP Filter: detaching [%s]\n", qif->qf_name);
1184                 *qp = qif->qf_next;
1185
1186                 /*
1187                  * Disable any rules directly associated with this interface
1188                  */
1189                 WRITE_ENTER(&ipf_nat);
1190                 for (np = nat_list; np; np = np->in_next)
1191                         if (np->in_ifp == (void *)qif->qf_ill)
1192                                 np->in_ifp = (struct ifnet *)-1;
1193                 RWLOCK_EXIT(&ipf_nat);
1194                 WRITE_ENTER(&ipf_mutex);
1195                 for (f = ipfilter[0][fr_active]; f; f = f->fr_next)
1196                         if (f->fr_ifa == (void *)qif->qf_ill)
1197                                 f->fr_ifa = (struct ifnet *)-1;
1198                 for (f = ipfilter[1][fr_active]; f; f = f->fr_next)
1199                         if (f->fr_ifa == (void *)qif->qf_ill)
1200                                 f->fr_ifa = (struct ifnet *)-1;
1201
1202 #if 0 /* XXX */
1203                 /*
1204                  * As well as the ill disappearing when a device is unplumb'd,
1205                  * it also appears that the associated queue structures also
1206                  * disappear - at least in the case of ppp, which is the most
1207                  * volatile here.  Thanks to Greg for finding this problem.
1208                  */
1209                 /*
1210                  * Restore q_qinfo pointers in interface queues
1211                  */
1212                 out = qif->qf_out;
1213                 in = qif->qf_in;
1214                 if (in) {
1215 # ifdef IPFDEBUG
1216                         cmn_err(CE_NOTE,
1217                                 "IP Filter: ipfsync: in queue(%lx)->q_qinfo FROM %lx TO %lx",
1218                                 in, in->q_qinfo, qif->qf_rqinfo
1219                                 );
1220 # endif
1221                         in->q_qinfo = qif->qf_rqinfo;
1222                 }
1223                 if (out) {
1224 # ifdef IPFDEBUG
1225                         cmn_err(CE_NOTE,
1226                                 "IP Filter: ipfsync: out queue(%lx)->q_qinfo FROM %lx TO %lx",
1227                                 out, out->q_qinfo, qif->qf_wqinfo
1228                                 );
1229 # endif
1230                         out->q_qinfo = qif->qf_wqinfo;
1231                 }
1232 #endif /* XXX */
1233                 RWLOCK_EXIT(&ipf_mutex);
1234                 KFREE(qif);
1235                 qif = *qp;
1236         }
1237         RWLOCK_EXIT(&ipfs_mutex);
1238         solattach();
1239
1240         /*
1241          * Resync. any NAT `connections' using this interface and its IP #.
1242          */
1243         for (il = ill_g_head; il; il = il->ill_next)
1244                 ip_natsync((void *)il);
1245         return 0;
1246 }
1247
1248
1249 /*
1250  * unhook the IP filter from all defined interfaces with IP addresses
1251  */
1252 int soldetach()
1253 {
1254         queue_t *in, *out;
1255         qif_t *qif, **qp;
1256         ill_t *il;
1257
1258         WRITE_ENTER(&ipfs_mutex);
1259         /*
1260          * Make two passes, first get rid of all the unknown devices, next
1261          * unlink known devices.
1262          */
1263         for (qp = &qif_head; (qif = *qp); ) {
1264                 for (il = ill_g_head; il; il = il->ill_next)
1265                         if (qif->qf_ill == il)
1266                                 break;
1267                 if (il) {
1268                         qp = &qif->qf_next;
1269                         continue;
1270                 }
1271                 cmn_err(CE_CONT, "IP Filter: removing [%s]\n", qif->qf_name);
1272                 *qp = qif->qf_next;
1273                 KFREE(qif);
1274         }
1275
1276         while ((qif = qif_head)) {
1277                 qif_head = qif->qf_next;
1278                 for (il = ill_g_head; il; il = il->ill_next)
1279                         if (qif->qf_ill == il)
1280                                 break;
1281                 if (il) {
1282                         in = qif->qf_in;
1283                         out = qif->qf_out;
1284                         cmn_err(CE_CONT, "IP Filter: detaching [%s,%d]\n",
1285                                 qif->qf_name, il->ill_ppa);
1286
1287 #ifdef  IPFDEBUG
1288                         cmn_err(CE_NOTE,
1289                                 "IP Filter: soldetach: in queue(%lx)->q_qinfo FROM %lx TO %lx",
1290                                 in, in->q_qinfo, qif->qf_rqinfo);
1291 #endif
1292                         in->q_qinfo = qif->qf_rqinfo;
1293
1294                         /*
1295                          * and the write queue...
1296                          */
1297 #ifdef  IPFDEBUG
1298                         cmn_err(CE_NOTE,
1299                                 "IP Filter: soldetach: out queue(%lx)->q_qinfo FROM %lx TO %lx",
1300                                 out, out->q_qinfo, qif->qf_wqinfo);
1301 #endif
1302                         out->q_qinfo = qif->qf_wqinfo;
1303                 }
1304                 KFREE(qif);
1305         }
1306         RWLOCK_EXIT(&ipfs_mutex);
1307         return ipldetach();
1308 }
1309
1310
1311 #ifdef  IPFDEBUG
1312 void printire(ire)
1313 ire_t *ire;
1314 {
1315         printf("ire: ll_hdr_mp %p rfq %p stq %p src_addr %x max_frag %d\n",
1316                 ire->ire_ll_hdr_mp, ire->ire_rfq, ire->ire_stq,
1317                 ire->ire_src_addr, ire->ire_max_frag);
1318         printf("ire: mask %x addr %x gateway_addr %x type %d\n",
1319                 ire->ire_mask, ire->ire_addr, ire->ire_gateway_addr,
1320                 ire->ire_type);
1321         printf("ire: ll_hdr_length %d ll_hdr_saved_mp %p\n",
1322                 ire->ire_ll_hdr_length, ire->ire_ll_hdr_saved_mp);
1323 }
1324 #endif
1325
1326
1327 int ipfr_fastroute(qf, ip, mb, mpp, fin, fdp)
1328 qif_t *qf;
1329 ip_t *ip;
1330 mblk_t *mb, **mpp;
1331 fr_info_t *fin;
1332 frdest_t *fdp;
1333 {
1334         ire_t *ir, *dir, *gw;
1335         struct in_addr dst;
1336         queue_t *q = NULL;
1337         mblk_t *mp = NULL;
1338         size_t hlen = 0;
1339         frentry_t *fr;
1340         void *ifp;
1341         u_char *s;
1342
1343 #ifndef sparc
1344         u_short __iplen, __ipoff;
1345 #endif
1346         /*
1347          * If this is a duplicate mblk then we want ip to point at that
1348          * data, not the original, if and only if it is already pointing at
1349          * the current mblk data.
1350          */
1351         if (ip == (ip_t *)qf->qf_m->b_rptr && qf->qf_m != mb)
1352                 ip = (ip_t *)mb->b_rptr;
1353
1354         /*
1355          * If there is another M_PROTO, we don't want it
1356          */
1357         if (*mpp != mb) {
1358                 (*mpp)->b_cont = NULL;
1359                 (*mpp)->b_prev = NULL;
1360                 freemsg(*mpp);
1361         }
1362
1363         ir = (ire_t *)fdp->fd_ifp;
1364
1365         if (fdp->fd_ip.s_addr)
1366                 dst = fdp->fd_ip;
1367         else
1368                 dst = fin->fin_fi.fi_dst;
1369
1370 #if SOLARIS2 > 5
1371         gw = NULL;
1372         dir = ire_route_lookup(dst.s_addr, 0xffffffff, 0, 0, NULL, &gw, NULL,
1373                                 MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT|
1374                                 MATCH_IRE_RECURSIVE);
1375 #else
1376         dir = ire_lookup(dst.s_addr);
1377 #endif
1378         if (dir)
1379                 if (!dir->ire_ll_hdr_mp || !dir->ire_ll_hdr_length)
1380                         dir = NULL;
1381
1382         if (!ir)
1383                 ir = dir;
1384
1385         if (ir && dir) {
1386                 ifp = ire_to_ill(ir);
1387                 fr = fin->fin_fr;
1388                 /*
1389                  * In case we're here due to "to <if>" being used with
1390                  * "keep state", check that we're going in the correct
1391                  * direction.
1392                  */
1393                 if ((fr != NULL) && (fdp->fd_ifp != NULL) &&
1394                     (fin->fin_rev != 0) && (fdp == &fr->fr_tif))
1395                         return -1;
1396
1397                 fin->fin_ifp == ifp;
1398                 if (fin->fin_out == 0) {
1399                         fin->fin_fr = ipacct[1][fr_active];
1400                         if ((fin->fin_fr != NULL) &&
1401                             (fr_scanlist(FR_NOMATCH, ip, fin, mb)&FR_ACCOUNT)){
1402                                 ATOMIC_INC(frstats[1].fr_acct);
1403                         }
1404                         fin->fin_fr = NULL;
1405                         (void) fr_checkstate(ip, fin);
1406                         (void) ip_natout(ip, fin);
1407                 }       
1408 #ifndef sparc
1409                 __iplen = (u_short)ip->ip_len,
1410                 __ipoff = (u_short)ip->ip_off;
1411
1412                 ip->ip_len = htons(__iplen);
1413                 ip->ip_off = htons(__ipoff);
1414 #endif
1415
1416                 if ((mp = dir->ire_ll_hdr_mp)) {
1417                         hlen = dir->ire_ll_hdr_length;
1418
1419                         s = mb->b_rptr;
1420                         if (hlen && (s - mb->b_datap->db_base) >= hlen) {
1421                                 s -= hlen;
1422                                 mb->b_rptr = (u_char *)s;
1423                                 bcopy((char *)mp->b_rptr, (char *)s, hlen);
1424                         } else {
1425                                 mblk_t  *mp2;
1426
1427                                 mp2 = copyb(mp);
1428                                 if (!mp2)
1429                                         goto bad_fastroute;
1430                                 mp2->b_cont = mb;
1431                                 mb = mp2;
1432                         }
1433                 }
1434
1435                 if (ir->ire_stq)
1436                         q = ir->ire_stq;
1437                 else if (ir->ire_rfq)
1438                         q = WR(ir->ire_rfq);
1439                 if (q) {
1440                         mb->b_prev = NULL;
1441                         RWLOCK_EXIT(&ipfs_mutex);
1442                         RWLOCK_EXIT(&ipf_solaris);
1443                         putnext(q, mb);
1444                         READ_ENTER(&ipf_solaris);
1445                         READ_ENTER(&ipfs_mutex);
1446                         ipl_frouteok[0]++;
1447                         return 0;
1448                 }
1449         }
1450 bad_fastroute:
1451         mb->b_prev = NULL;
1452         freemsg(mb);
1453         ipl_frouteok[1]++;
1454         return -1;
1455 }
1456
1457
1458 void copyout_mblk(m, off, len, buf)
1459 mblk_t *m;
1460 size_t off, len;
1461 char *buf;
1462 {
1463         u_char *s, *bp = (u_char *)buf;
1464         size_t mlen, olen, clen;
1465
1466         for (; m && len; m = m->b_cont) {
1467                 if (MTYPE(m) != M_DATA)
1468                         continue;
1469                 s = m->b_rptr;
1470                 mlen = m->b_wptr - s;
1471                 olen = MIN(off, mlen);
1472                 if ((olen == mlen) || (olen < off)) {
1473                         off -= olen;
1474                         continue;
1475                 } else if (olen) {
1476                         off -= olen;
1477                         s += olen;
1478                         mlen -= olen;
1479                 }
1480                 clen = MIN(mlen, len);
1481                 bcopy(s, bp, clen);
1482                 len -= clen;
1483                 bp += clen;
1484         }
1485 }
1486
1487
1488 void copyin_mblk(m, off, len, buf)
1489 mblk_t *m;
1490 size_t off, len;
1491 char *buf;
1492 {
1493         u_char *s, *bp = (u_char *)buf;
1494         size_t mlen, olen, clen;
1495
1496         for (; m && len; m = m->b_cont) {
1497                 if (MTYPE(m) != M_DATA)
1498                         continue;
1499                 s = m->b_rptr;
1500                 mlen = m->b_wptr - s;
1501                 olen = MIN(off, mlen);
1502                 if ((olen == mlen) || (olen < off)) {
1503                         off -= olen;
1504                         continue;
1505                 } else if (olen) {
1506                         off -= olen;
1507                         s += olen;
1508                         mlen -= olen;
1509                 }
1510                 clen = MIN(mlen, len);
1511                 bcopy(bp, s, clen);
1512                 len -= clen;
1513                 bp += clen;
1514         }
1515 }