1 /* $OpenBSD: if_pflog.c,v 1.69 2015/02/13 13:35:03 millert Exp $ */ 2 /* 3 * The authors of this code are John Ioannidis (ji@tla.org), 4 * Angelos D. Keromytis (kermit@csd.uch.gr) and 5 * Niels Provos (provos@physnet.uni-hamburg.de). 6 * 7 * This code was written by John Ioannidis for BSD/OS in Athens, Greece, 8 * in November 1995. 9 * 10 * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, 11 * by Angelos D. Keromytis. 12 * 13 * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis 14 * and Niels Provos. 15 * 16 * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis 17 * and Niels Provos. 18 * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos. 19 * Copyright (c) 2002 - 2010 Henning Brauer 20 * 21 * Permission to use, copy, and modify this software with or without fee 22 * is hereby granted, provided that this entire notice is included in 23 * all copies of any software which is or includes a copy or 24 * modification of this software. 25 * You may use this code under the GNU public license if you so wish. Please 26 * contribute changes back to the authors under this freer than GPL license 27 * so that we may further the use of strong encryption without limitations to 28 * all. 29 * 30 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR 31 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY 32 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE 33 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR 34 * PURPOSE. 35 */ 36 37 #include "bpfilter.h" 38 #include "pflog.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/mbuf.h> 43 #include <sys/proc.h> 44 #include <sys/socket.h> 45 #include <sys/stdint.h> 46 #include <sys/ioctl.h> 47 48 #include <net/if.h> 49 #include <net/if_var.h> 50 #include <net/if_types.h> 51 #include <net/bpf.h> 52 53 #include <netinet/in.h> 54 #include <netinet/ip.h> 55 #include <netinet/tcp.h> 56 #include <netinet/udp.h> 57 #include <netinet/ip_icmp.h> 58 59 #ifdef INET6 60 #include <netinet/ip6.h> 61 #include <netinet/icmp6.h> 62 #endif /* INET6 */ 63 64 #include <net/pfvar.h> 65 #include <net/if_pflog.h> 66 67 #define PFLOGMTU (32768 + MHLEN + MLEN) 68 69 #ifdef PFLOGDEBUG 70 #define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0) 71 #else 72 #define DPRINTF(x) 73 #endif 74 75 void pflogattach(int); 76 int pflogifs_resize(size_t); 77 int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 78 struct rtentry *); 79 int pflogioctl(struct ifnet *, u_long, caddr_t); 80 void pflogstart(struct ifnet *); 81 int pflog_clone_create(struct if_clone *, int); 82 int pflog_clone_destroy(struct ifnet *); 83 void pflog_bpfcopy(const void *, void *, size_t); 84 85 LIST_HEAD(, pflog_softc) pflogif_list; 86 struct if_clone pflog_cloner = 87 IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy); 88 89 int npflogifs = 0; 90 struct ifnet **pflogifs = NULL; /* for fast access */ 91 struct mbuf *pflog_mhdr = NULL, *pflog_mptr = NULL; 92 93 void 94 pflogattach(int npflog) 95 { 96 LIST_INIT(&pflogif_list); 97 if (pflog_mhdr == NULL) 98 if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL) 99 panic("pflogattach: no mbuf"); 100 if (pflog_mptr == NULL) 101 if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL) 102 panic("pflogattach: no mbuf"); 103 if_clone_attach(&pflog_cloner); 104 } 105 106 int 107 pflogifs_resize(size_t n) 108 { 109 struct ifnet **p; 110 int i; 111 112 if (n > SIZE_MAX / sizeof(*p)) 113 return (EINVAL); 114 if (n == 0) 115 p = NULL; 116 else 117 if ((p = mallocarray(n, sizeof(*p), M_DEVBUF, 118 M_NOWAIT|M_ZERO)) == NULL) 119 return (ENOMEM); 120 for (i = 0; i < n; i++) 121 if (i < npflogifs) 122 p[i] = pflogifs[i]; 123 else 124 p[i] = NULL; 125 126 if (pflogifs) 127 free(pflogifs, M_DEVBUF, 0); 128 pflogifs = p; 129 npflogifs = n; 130 return (0); 131 } 132 133 int 134 pflog_clone_create(struct if_clone *ifc, int unit) 135 { 136 struct ifnet *ifp; 137 struct pflog_softc *pflogif; 138 int s; 139 140 if ((pflogif = malloc(sizeof(*pflogif), 141 M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 142 return (ENOMEM); 143 144 pflogif->sc_unit = unit; 145 ifp = &pflogif->sc_if; 146 snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit); 147 ifp->if_softc = pflogif; 148 ifp->if_mtu = PFLOGMTU; 149 ifp->if_ioctl = pflogioctl; 150 ifp->if_output = pflogoutput; 151 ifp->if_start = pflogstart; 152 ifp->if_type = IFT_PFLOG; 153 IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 154 ifp->if_hdrlen = PFLOG_HDRLEN; 155 if_attach(ifp); 156 if_alloc_sadl(ifp); 157 158 #if NBPFILTER > 0 159 bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN); 160 #endif 161 162 s = splnet(); 163 LIST_INSERT_HEAD(&pflogif_list, pflogif, sc_list); 164 if (unit + 1 > npflogifs && pflogifs_resize(unit + 1) != 0) { 165 splx(s); 166 return (ENOMEM); 167 } 168 pflogifs[unit] = ifp; 169 splx(s); 170 171 return (0); 172 } 173 174 int 175 pflog_clone_destroy(struct ifnet *ifp) 176 { 177 struct pflog_softc *pflogif = ifp->if_softc; 178 int s, i; 179 180 s = splnet(); 181 pflogifs[pflogif->sc_unit] = NULL; 182 LIST_REMOVE(pflogif, sc_list); 183 184 for (i = npflogifs; i > 0 && pflogifs[i - 1] == NULL; i--) 185 ; /* nothing */ 186 if (i < npflogifs) 187 pflogifs_resize(i); /* error harmless here */ 188 splx(s); 189 190 if_detach(ifp); 191 free(pflogif, M_DEVBUF, 0); 192 return (0); 193 } 194 195 /* 196 * Start output on the pflog interface. 197 */ 198 void 199 pflogstart(struct ifnet *ifp) 200 { 201 struct mbuf *m; 202 int s; 203 204 for (;;) { 205 s = splnet(); 206 IF_DROP(&ifp->if_snd); 207 IF_DEQUEUE(&ifp->if_snd, m); 208 splx(s); 209 210 if (m == NULL) 211 return; 212 else 213 m_freem(m); 214 } 215 } 216 217 int 218 pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 219 struct rtentry *rt) 220 { 221 m_freem(m); 222 return (0); 223 } 224 225 /* ARGSUSED */ 226 int 227 pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 228 { 229 switch (cmd) { 230 case SIOCSIFFLAGS: 231 if (ifp->if_flags & IFF_UP) 232 ifp->if_flags |= IFF_RUNNING; 233 else 234 ifp->if_flags &= ~IFF_RUNNING; 235 break; 236 default: 237 return (ENOTTY); 238 } 239 240 return (0); 241 } 242 243 int 244 pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm, 245 struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *trigger) 246 { 247 #if NBPFILTER > 0 248 struct ifnet *ifn; 249 struct pfloghdr hdr; 250 251 if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL) 252 return (-1); 253 if (trigger == NULL) 254 trigger = rm; 255 256 if (trigger->logif >= npflogifs || (ifn = pflogifs[trigger->logif]) == 257 NULL || !ifn->if_bpf) 258 return (0); 259 260 bzero(&hdr, sizeof(hdr)); 261 hdr.length = PFLOG_REAL_HDRLEN; 262 hdr.action = rm->action; 263 hdr.reason = reason; 264 memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname)); 265 266 if (am == NULL) { 267 hdr.rulenr = htonl(rm->nr); 268 hdr.subrulenr = -1; 269 } else { 270 hdr.rulenr = htonl(am->nr); 271 hdr.subrulenr = htonl(rm->nr); 272 if (ruleset != NULL && ruleset->anchor != NULL) 273 strlcpy(hdr.ruleset, ruleset->anchor->name, 274 sizeof(hdr.ruleset)); 275 } 276 if (trigger->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done) 277 pd->lookup.done = pf_socket_lookup(pd); 278 if (pd->lookup.done > 0) { 279 hdr.uid = pd->lookup.uid; 280 hdr.pid = pd->lookup.pid; 281 } else { 282 hdr.uid = UID_MAX; 283 hdr.pid = NO_PID; 284 } 285 hdr.rule_uid = rm->cuid; 286 hdr.rule_pid = rm->cpid; 287 hdr.dir = pd->dir; 288 289 PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->naf); 290 PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->naf); 291 hdr.af = pd->af; 292 hdr.naf = pd->naf; 293 hdr.sport = pd->nsport; 294 hdr.dport = pd->ndport; 295 296 ifn->if_opackets++; 297 ifn->if_obytes += pd->m->m_pkthdr.len; 298 299 bpf_mtap_hdr(ifn->if_bpf, (caddr_t)&hdr, PFLOG_HDRLEN, pd->m, 300 BPF_DIRECTION_OUT, pflog_bpfcopy); 301 #endif 302 303 return (0); 304 } 305 306 void 307 pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len) 308 { 309 struct mbuf *m, *mp, *mhdr, *mptr; 310 struct pfloghdr *pfloghdr; 311 u_int count; 312 u_char *dst, *mdst; 313 int afto, hlen, mlen, off; 314 union pf_headers { 315 struct tcphdr tcp; 316 struct udphdr udp; 317 struct icmp icmp; 318 #ifdef INET6 319 struct icmp6_hdr icmp6; 320 struct mld_hdr mld; 321 struct nd_neighbor_solicit nd_ns; 322 #endif /* INET6 */ 323 } pdhdrs; 324 325 struct pf_pdesc pd; 326 struct pf_addr osaddr, odaddr; 327 u_int16_t osport = 0, odport = 0; 328 u_int8_t proto = 0; 329 330 m = (struct mbuf *)src_arg; 331 dst = dst_arg; 332 333 mhdr = pflog_mhdr; 334 mptr = pflog_mptr; 335 336 if (m == NULL) 337 panic("pflog_bpfcopy got no mbuf"); 338 339 /* first mbuf holds struct pfloghdr */ 340 pfloghdr = mtod(m, struct pfloghdr *); 341 afto = pfloghdr->af != pfloghdr->naf; 342 count = min(m->m_len, len); 343 bcopy(pfloghdr, dst, count); 344 pfloghdr = (struct pfloghdr *)dst; 345 dst += count; 346 len -= count; 347 m = m->m_next; 348 349 if (len <= 0) 350 return; 351 352 /* second mbuf is pkthdr */ 353 if (m == NULL) 354 panic("no second mbuf"); 355 356 /* 357 * temporary mbuf will hold an ip/ip6 header and 8 bytes 358 * of the protocol header 359 */ 360 m_inithdr(mhdr); 361 mhdr->m_len = 0; /* XXX not done in m_inithdr() */ 362 363 #if INET && INET6 364 /* offset for a new header */ 365 if (afto && pfloghdr->af == AF_INET) 366 mhdr->m_data += sizeof(struct ip6_hdr) - 367 sizeof(struct ip); 368 #endif /* INET && INET6 */ 369 370 mdst = mtod(mhdr, char *); 371 switch (pfloghdr->af) { 372 case AF_INET: { 373 struct ip *h; 374 375 if (m->m_pkthdr.len < sizeof(*h)) 376 goto copy; 377 m_copydata(m, 0, sizeof(*h), mdst); 378 h = (struct ip *)mdst; 379 hlen = h->ip_hl << 2; 380 if (hlen > sizeof(*h) && (m->m_pkthdr.len >= hlen)) 381 m_copydata(m, sizeof(*h), hlen - sizeof(*h), 382 mdst + sizeof(*h)); 383 break; 384 } 385 #ifdef INET6 386 case AF_INET6: { 387 struct ip6_hdr *h; 388 389 if (m->m_pkthdr.len < sizeof(*h)) 390 goto copy; 391 hlen = sizeof(struct ip6_hdr); 392 m_copydata(m, 0, hlen, mdst); 393 h = (struct ip6_hdr *)mdst; 394 proto = h->ip6_nxt; 395 break; 396 } 397 #endif /* INET6 */ 398 default: 399 /* shouldn't happen ever :-) */ 400 goto copy; 401 } 402 403 if (m->m_pkthdr.len < hlen + 8 && proto != IPPROTO_NONE) 404 goto copy; 405 else if (proto != IPPROTO_NONE) { 406 /* copy 8 bytes of the protocol header */ 407 m_copydata(m, hlen, 8, mdst + hlen); 408 hlen += 8; 409 } 410 411 mhdr->m_len += hlen; 412 mhdr->m_pkthdr.len = mhdr->m_len; 413 414 /* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen) */ 415 mp = m_getptr(m, hlen, &off); 416 if (mp != NULL) { 417 bcopy(mp, mptr, sizeof(*mptr)); 418 mptr->m_data += off; 419 mptr->m_len -= off; 420 mptr->m_flags &= ~M_PKTHDR; 421 mhdr->m_next = mptr; 422 mhdr->m_pkthdr.len += m->m_pkthdr.len - hlen; 423 } 424 425 /* 426 * Rewrite addresses if needed. Reason pointer must be NULL to avoid 427 * counting the packet here again. 428 */ 429 if (pf_setup_pdesc(&pd, &pdhdrs, pfloghdr->af, pfloghdr->dir, NULL, 430 mhdr, NULL) != PF_PASS) 431 goto copy; 432 pd.naf = pfloghdr->naf; 433 434 PF_ACPY(&osaddr, pd.src, pd.af); 435 PF_ACPY(&odaddr, pd.dst, pd.af); 436 if (pd.sport) 437 osport = *pd.sport; 438 if (pd.dport) 439 odport = *pd.dport; 440 441 if (pd.virtual_proto != PF_VPROTO_FRAGMENT && 442 (pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr, 443 pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0, 444 pfloghdr->dir))) { 445 m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen), 446 pd.hdr.any, M_NOWAIT); 447 #if INET && INET6 448 if (afto) { 449 PF_ACPY(&pd.nsaddr, &pfloghdr->saddr, pd.naf); 450 PF_ACPY(&pd.ndaddr, &pfloghdr->daddr, pd.naf); 451 } 452 #endif /* INET && INET6 */ 453 PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af); 454 PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af); 455 pfloghdr->sport = osport; 456 pfloghdr->dport = odport; 457 } 458 459 pd.tot_len = min(pd.tot_len, len); 460 pd.tot_len -= pd.m->m_data - pd.m->m_pktdat; 461 462 #if INET && INET6 463 if (afto && pfloghdr->rewritten) 464 pf_translate_af(&pd); 465 #endif /* INET && INET6 */ 466 467 m = pd.m; 468 copy: 469 mlen = min(m->m_pkthdr.len, len); 470 m_copydata(m, 0, mlen, dst); 471 len -= mlen; 472 if (len > 0) 473 bzero(dst + mlen, len); 474 } 475