ip_fw_log.c revision 238277
1/*- 2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_log.c 238277 2012-07-09 07:16:19Z hrs $"); 28 29/* 30 * Logging support for ipfw 31 */ 32 33#include "opt_ipfw.h" 34#include "opt_inet.h" 35#ifndef INET 36#error IPFIREWALL requires INET. 37#endif /* INET */ 38#include "opt_inet6.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/mbuf.h> 43#include <sys/kernel.h> 44#include <sys/socket.h> 45#include <sys/sysctl.h> 46#include <sys/syslog.h> 47#include <sys/lock.h> 48#include <sys/rwlock.h> 49#include <net/ethernet.h> /* for ETHERTYPE_IP */ 50#include <net/if.h> 51#include <net/if_clone.h> 52#include <net/vnet.h> 53#include <net/if_types.h> /* for IFT_ETHER */ 54#include <net/bpf.h> /* for BPF */ 55 56#include <netinet/in.h> 57#include <netinet/ip.h> 58#include <netinet/ip_icmp.h> 59#include <netinet/ip_var.h> 60#include <netinet/ip_fw.h> 61#include <netinet/ipfw/ip_fw_private.h> 62#include <netinet/tcp_var.h> 63#include <netinet/udp.h> 64 65#include <netinet/ip6.h> 66#include <netinet/icmp6.h> 67#ifdef INET6 68#include <netinet6/in6_var.h> /* ip6_sprintf() */ 69#endif 70 71#ifdef MAC 72#include <security/mac/mac_framework.h> 73#endif 74 75/* 76 * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T 77 * Other macros just cast void * into the appropriate type 78 */ 79#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) 80#define TCP(p) ((struct tcphdr *)(p)) 81#define SCTP(p) ((struct sctphdr *)(p)) 82#define UDP(p) ((struct udphdr *)(p)) 83#define ICMP(p) ((struct icmphdr *)(p)) 84#define ICMP6(p) ((struct icmp6_hdr *)(p)) 85 86#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 87#define SNP(buf) buf, sizeof(buf) 88 89#ifdef WITHOUT_BPF 90void 91ipfw_log_bpf(int onoff) 92{ 93} 94#else /* !WITHOUT_BPF */ 95static struct ifnet *log_if; /* hook to attach to bpf */ 96static struct rwlock log_if_lock; 97#define LOGIF_LOCK_INIT(x) rw_init(&log_if_lock, "ipfw log_if lock") 98#define LOGIF_LOCK_DESTROY(x) rw_destroy(&log_if_lock) 99#define LOGIF_RLOCK(x) rw_rlock(&log_if_lock) 100#define LOGIF_RUNLOCK(x) rw_runlock(&log_if_lock) 101#define LOGIF_WLOCK(x) rw_wlock(&log_if_lock) 102#define LOGIF_WUNLOCK(x) rw_wunlock(&log_if_lock) 103 104#define IPFWNAME "ipfw" 105 106/* we use this dummy function for all ifnet callbacks */ 107static int 108log_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr) 109{ 110 return EINVAL; 111} 112 113static int 114ipfw_log_output(struct ifnet *ifp, struct mbuf *m, 115 struct sockaddr *dst, struct route *ro) 116{ 117 if (m != NULL) 118 m_freem(m); 119 return EINVAL; 120} 121 122static void 123ipfw_log_start(struct ifnet* ifp) 124{ 125 panic("ipfw_log_start() must not be called"); 126} 127 128static const u_char ipfwbroadcastaddr[6] = 129 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 130 131static int 132ipfw_log_clone_match(struct if_clone *ifc, const char *name) 133{ 134 135 return (strncmp(name, IPFWNAME, sizeof(IPFWNAME) - 1) == 0); 136} 137 138static int 139ipfw_log_clone_create(struct if_clone *ifc, char *name, size_t len, 140 caddr_t params) 141{ 142 int error; 143 int unit; 144 struct ifnet *ifp; 145 146 error = ifc_name2unit(name, &unit); 147 if (error) 148 return (error); 149 150 error = ifc_alloc_unit(ifc, &unit); 151 if (error) 152 return (error); 153 154 ifp = if_alloc(IFT_ETHER); 155 if (ifp == NULL) { 156 ifc_free_unit(ifc, unit); 157 return (ENOSPC); 158 } 159 ifp->if_dname = IPFWNAME; 160 ifp->if_dunit = unit; 161 snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", IPFWNAME, unit); 162 strlcpy(name, ifp->if_xname, len); 163 ifp->if_mtu = 65536; 164 ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST; 165 ifp->if_init = (void *)log_dummy; 166 ifp->if_ioctl = log_dummy; 167 ifp->if_start = ipfw_log_start; 168 ifp->if_output = ipfw_log_output; 169 ifp->if_addrlen = 6; 170 ifp->if_hdrlen = 14; 171 ifp->if_broadcastaddr = ipfwbroadcastaddr; 172 ifp->if_baudrate = IF_Mbps(10); 173 174 LOGIF_WLOCK(); 175 if (log_if == NULL) 176 log_if = ifp; 177 else { 178 LOGIF_WUNLOCK(); 179 if_free(ifp); 180 ifc_free_unit(ifc, unit); 181 return (EEXIST); 182 } 183 LOGIF_WUNLOCK(); 184 if_attach(ifp); 185 bpfattach(ifp, DLT_EN10MB, 14); 186 187 return (0); 188} 189 190static int 191ipfw_log_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) 192{ 193 int unit; 194 195 if (ifp == NULL) 196 return (0); 197 198 LOGIF_WLOCK(); 199 if (log_if != NULL && ifp == log_if) 200 log_if = NULL; 201 else { 202 LOGIF_WUNLOCK(); 203 return (EINVAL); 204 } 205 LOGIF_WUNLOCK(); 206 207 unit = ifp->if_dunit; 208 bpfdetach(ifp); 209 if_detach(ifp); 210 if_free(ifp); 211 ifc_free_unit(ifc, unit); 212 213 return (0); 214} 215 216static struct if_clone ipfw_log_cloner = IFC_CLONE_INITIALIZER( 217 IPFWNAME, NULL, IF_MAXUNIT, 218 NULL, ipfw_log_clone_match, ipfw_log_clone_create, ipfw_log_clone_destroy); 219 220void 221ipfw_log_bpf(int onoff) 222{ 223 224 if (onoff) { 225 LOGIF_LOCK_INIT(); 226 if_clone_attach(&ipfw_log_cloner); 227 } else { 228 if_clone_detach(&ipfw_log_cloner); 229 LOGIF_LOCK_DESTROY(); 230 } 231} 232#endif /* !WITHOUT_BPF */ 233 234/* 235 * We enter here when we have a rule with O_LOG. 236 * XXX this function alone takes about 2Kbytes of code! 237 */ 238void 239ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, 240 struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, 241 struct ip *ip) 242{ 243 char *action; 244 int limit_reached = 0; 245 char action2[92], proto[128], fragment[32]; 246 247 if (V_fw_verbose == 0) { 248#ifndef WITHOUT_BPF 249 LOGIF_RLOCK(); 250 if (log_if == NULL || log_if->if_bpf == NULL) { 251 LOGIF_RUNLOCK(); 252 return; 253 } 254 255 if (args->eh) /* layer2, use orig hdr */ 256 BPF_MTAP2(log_if, args->eh, ETHER_HDR_LEN, m); 257 else 258 /* Add fake header. Later we will store 259 * more info in the header. 260 */ 261 BPF_MTAP2(log_if, "DDDDDDSSSSSS\x08\x00", ETHER_HDR_LEN, m); 262 LOGIF_RUNLOCK(); 263#endif /* !WITHOUT_BPF */ 264 return; 265 } 266 /* the old 'log' function */ 267 fragment[0] = '\0'; 268 proto[0] = '\0'; 269 270 if (f == NULL) { /* bogus pkt */ 271 if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit) 272 return; 273 V_norule_counter++; 274 if (V_norule_counter == V_verbose_limit) 275 limit_reached = V_verbose_limit; 276 action = "Refuse"; 277 } else { /* O_LOG is the first action, find the real one */ 278 ipfw_insn *cmd = ACTION_PTR(f); 279 ipfw_insn_log *l = (ipfw_insn_log *)cmd; 280 281 if (l->max_log != 0 && l->log_left == 0) 282 return; 283 l->log_left--; 284 if (l->log_left == 0) 285 limit_reached = l->max_log; 286 cmd += F_LEN(cmd); /* point to first action */ 287 if (cmd->opcode == O_ALTQ) { 288 ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; 289 290 snprintf(SNPARGS(action2, 0), "Altq %d", 291 altq->qid); 292 cmd += F_LEN(cmd); 293 } 294 if (cmd->opcode == O_PROB) 295 cmd += F_LEN(cmd); 296 297 if (cmd->opcode == O_TAG) 298 cmd += F_LEN(cmd); 299 300 action = action2; 301 switch (cmd->opcode) { 302 case O_DENY: 303 action = "Deny"; 304 break; 305 306 case O_REJECT: 307 if (cmd->arg1==ICMP_REJECT_RST) 308 action = "Reset"; 309 else if (cmd->arg1==ICMP_UNREACH_HOST) 310 action = "Reject"; 311 else 312 snprintf(SNPARGS(action2, 0), "Unreach %d", 313 cmd->arg1); 314 break; 315 316 case O_UNREACH6: 317 if (cmd->arg1==ICMP6_UNREACH_RST) 318 action = "Reset"; 319 else 320 snprintf(SNPARGS(action2, 0), "Unreach %d", 321 cmd->arg1); 322 break; 323 324 case O_ACCEPT: 325 action = "Accept"; 326 break; 327 case O_COUNT: 328 action = "Count"; 329 break; 330 case O_DIVERT: 331 snprintf(SNPARGS(action2, 0), "Divert %d", 332 cmd->arg1); 333 break; 334 case O_TEE: 335 snprintf(SNPARGS(action2, 0), "Tee %d", 336 cmd->arg1); 337 break; 338 case O_SETFIB: 339 snprintf(SNPARGS(action2, 0), "SetFib %d", 340 cmd->arg1); 341 break; 342 case O_SKIPTO: 343 snprintf(SNPARGS(action2, 0), "SkipTo %d", 344 cmd->arg1); 345 break; 346 case O_PIPE: 347 snprintf(SNPARGS(action2, 0), "Pipe %d", 348 cmd->arg1); 349 break; 350 case O_QUEUE: 351 snprintf(SNPARGS(action2, 0), "Queue %d", 352 cmd->arg1); 353 break; 354 case O_FORWARD_IP: { 355 ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd; 356 int len; 357 struct in_addr dummyaddr; 358 if (sa->sa.sin_addr.s_addr == INADDR_ANY) 359 dummyaddr.s_addr = htonl(tablearg); 360 else 361 dummyaddr.s_addr = sa->sa.sin_addr.s_addr; 362 363 len = snprintf(SNPARGS(action2, 0), "Forward to %s", 364 inet_ntoa(dummyaddr)); 365 366 if (sa->sa.sin_port) 367 snprintf(SNPARGS(action2, len), ":%d", 368 sa->sa.sin_port); 369 } 370 break; 371#ifdef INET6 372 case O_FORWARD_IP6: { 373 char buf[INET6_ADDRSTRLEN]; 374 ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd; 375 int len; 376 377 len = snprintf(SNPARGS(action2, 0), "Forward to [%s]", 378 ip6_sprintf(buf, &sa->sa.sin6_addr)); 379 380 if (sa->sa.sin6_port) 381 snprintf(SNPARGS(action2, len), ":%u", 382 sa->sa.sin6_port); 383 } 384 break; 385#endif 386 case O_NETGRAPH: 387 snprintf(SNPARGS(action2, 0), "Netgraph %d", 388 cmd->arg1); 389 break; 390 case O_NGTEE: 391 snprintf(SNPARGS(action2, 0), "Ngtee %d", 392 cmd->arg1); 393 break; 394 case O_NAT: 395 action = "Nat"; 396 break; 397 case O_REASS: 398 action = "Reass"; 399 break; 400 case O_CALLRETURN: 401 if (cmd->len & F_NOT) 402 action = "Return"; 403 else 404 snprintf(SNPARGS(action2, 0), "Call %d", 405 cmd->arg1); 406 break; 407 default: 408 action = "UNKNOWN"; 409 break; 410 } 411 } 412 413 if (hlen == 0) { /* non-ip */ 414 snprintf(SNPARGS(proto, 0), "MAC"); 415 416 } else { 417 int len; 418#ifdef INET6 419 char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2]; 420#else 421 char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 422#endif 423 struct icmphdr *icmp; 424 struct tcphdr *tcp; 425 struct udphdr *udp; 426#ifdef INET6 427 struct ip6_hdr *ip6 = NULL; 428 struct icmp6_hdr *icmp6; 429 u_short ip6f_mf; 430#endif 431 src[0] = '\0'; 432 dst[0] = '\0'; 433#ifdef INET6 434 ip6f_mf = offset & IP6F_MORE_FRAG; 435 offset &= IP6F_OFF_MASK; 436 437 if (IS_IP6_FLOW_ID(&(args->f_id))) { 438 char ip6buf[INET6_ADDRSTRLEN]; 439 snprintf(src, sizeof(src), "[%s]", 440 ip6_sprintf(ip6buf, &args->f_id.src_ip6)); 441 snprintf(dst, sizeof(dst), "[%s]", 442 ip6_sprintf(ip6buf, &args->f_id.dst_ip6)); 443 444 ip6 = (struct ip6_hdr *)ip; 445 tcp = (struct tcphdr *)(((char *)ip) + hlen); 446 udp = (struct udphdr *)(((char *)ip) + hlen); 447 } else 448#endif 449 { 450 tcp = L3HDR(struct tcphdr, ip); 451 udp = L3HDR(struct udphdr, ip); 452 453 inet_ntoa_r(ip->ip_src, src); 454 inet_ntoa_r(ip->ip_dst, dst); 455 } 456 457 switch (args->f_id.proto) { 458 case IPPROTO_TCP: 459 len = snprintf(SNPARGS(proto, 0), "TCP %s", src); 460 if (offset == 0) 461 snprintf(SNPARGS(proto, len), ":%d %s:%d", 462 ntohs(tcp->th_sport), 463 dst, 464 ntohs(tcp->th_dport)); 465 else 466 snprintf(SNPARGS(proto, len), " %s", dst); 467 break; 468 469 case IPPROTO_UDP: 470 len = snprintf(SNPARGS(proto, 0), "UDP %s", src); 471 if (offset == 0) 472 snprintf(SNPARGS(proto, len), ":%d %s:%d", 473 ntohs(udp->uh_sport), 474 dst, 475 ntohs(udp->uh_dport)); 476 else 477 snprintf(SNPARGS(proto, len), " %s", dst); 478 break; 479 480 case IPPROTO_ICMP: 481 icmp = L3HDR(struct icmphdr, ip); 482 if (offset == 0) 483 len = snprintf(SNPARGS(proto, 0), 484 "ICMP:%u.%u ", 485 icmp->icmp_type, icmp->icmp_code); 486 else 487 len = snprintf(SNPARGS(proto, 0), "ICMP "); 488 len += snprintf(SNPARGS(proto, len), "%s", src); 489 snprintf(SNPARGS(proto, len), " %s", dst); 490 break; 491#ifdef INET6 492 case IPPROTO_ICMPV6: 493 icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen); 494 if (offset == 0) 495 len = snprintf(SNPARGS(proto, 0), 496 "ICMPv6:%u.%u ", 497 icmp6->icmp6_type, icmp6->icmp6_code); 498 else 499 len = snprintf(SNPARGS(proto, 0), "ICMPv6 "); 500 len += snprintf(SNPARGS(proto, len), "%s", src); 501 snprintf(SNPARGS(proto, len), " %s", dst); 502 break; 503#endif 504 default: 505 len = snprintf(SNPARGS(proto, 0), "P:%d %s", 506 args->f_id.proto, src); 507 snprintf(SNPARGS(proto, len), " %s", dst); 508 break; 509 } 510 511#ifdef INET6 512 if (IS_IP6_FLOW_ID(&(args->f_id))) { 513 if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) 514 snprintf(SNPARGS(fragment, 0), 515 " (frag %08x:%d@%d%s)", 516 args->f_id.extra, 517 ntohs(ip6->ip6_plen) - hlen, 518 ntohs(offset) << 3, ip6f_mf ? "+" : ""); 519 } else 520#endif 521 { 522 int ipoff, iplen; 523 ipoff = ntohs(ip->ip_off); 524 iplen = ntohs(ip->ip_len); 525 if (ipoff & (IP_MF | IP_OFFMASK)) 526 snprintf(SNPARGS(fragment, 0), 527 " (frag %d:%d@%d%s)", 528 ntohs(ip->ip_id), iplen - (ip->ip_hl << 2), 529 offset << 3, 530 (ipoff & IP_MF) ? "+" : ""); 531 } 532 } 533#ifdef __FreeBSD__ 534 if (oif || m->m_pkthdr.rcvif) 535 log(LOG_SECURITY | LOG_INFO, 536 "ipfw: %d %s %s %s via %s%s\n", 537 f ? f->rulenum : -1, 538 action, proto, oif ? "out" : "in", 539 oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname, 540 fragment); 541 else 542#endif 543 log(LOG_SECURITY | LOG_INFO, 544 "ipfw: %d %s %s [no if info]%s\n", 545 f ? f->rulenum : -1, 546 action, proto, fragment); 547 if (limit_reached) 548 log(LOG_SECURITY | LOG_NOTICE, 549 "ipfw: limit %d reached on entry %d\n", 550 limit_reached, f ? f->rulenum : -1); 551} 552/* end of file */ 553