pfild.c revision 381a2a9a387f449fab7d0c7e97c4184c26963abf
/*
* Copyright (C) 2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <malloc.h>
#include <stropts.h>
#include <stdlib.h>
#include "pfild.h"
extern int vas(const struct pfil_ifaddrs *, int);
/*
* pfild.c: interface data and packet transmission daemon for pfil
*
* pfild provides the pfil kernel module with certain data that are not
* directly available to kernel code using supported OS interfaces. pfild
* accesses the routing tables and network interface parameters using
* interfaces readily available to a user space daemon, copies the data into
*
* pfild also provides a way for the kernel module to originate IP packets
* without resorting to unsupported kernel interfaces. If the kernel
* sends up an M_DATA message, pfild sends it on a raw IP socket so that it
* gets routed and transmitted as a normal packet.
*/
/* file descriptors for talking to pfil, ifnet, routing kernel modules */
/*
* flag indicates that some interface or routing data have changed since
* last update.
*/
static int flag = 1;
/*
* debuglevel indicates to what level debugging messages should be emitted.
*/
static int debuglevel = 0;
/* Wait for this many ms of quiet time after changes before doing an update. */
#define QUIETTIME 200
/*
* Send a message to the pfil kernel module.
* Returns zero for success, otherwise non-zero with errror in errno.
*/
int
{
int error;
if (debuglevel > 0)
if (pfil_fd >= 0) {
if (debuglevel > 0)
"pfild:pfil_msg():putmsg(%d,%p,%p,0) = %d\n",
} else {
error = 0;
if (debuglevel > 0)
"pfild:pfil_msg():pfil_fd < 0\n");
}
return (error);
}
/*
* Handle a PF_ROUTE message. If an address has been added or deleted, treat
* this as an indication that some interface data has been udpated. If a route
* has been added or deleted, treat this as an indication that the routing
* table has been updated. The current implementation completely updates both
* sets of data when either kind of change is indicated.
*
* p points to, and size indicates the size of, the message.
*/
static void
{
if (size < sizeof (*p) ||
size < p->ifam_msglen ||
p->ifam_version != RTM_VERSION) {
if (debuglevel > 0)
"Not a valid version %u RTM message - "
"%u bytes version %u\n",
return;
}
switch (p->ifam_type) {
case RTM_NEWADDR:
case RTM_DELADDR:
case RTM_ADD:
case RTM_DELETE:
flag = 1;
break;
default:
break;
}
if (debuglevel > 0)
"pfild:handle_msg(): msg rcvd %d flag %d\n",
}
static const char *
dumpaddr(void *p)
{
static char buf[INET6_ADDRSTRLEN];
struct sockaddr_in *sin = p;
struct sockaddr_in6 *sin6 = p;
switch (sin->sin_family) {
case AF_INET:
sizeof (buf)));
case AF_INET6:
sizeof (buf)));
default:
return ("<none>");
}
}
#define ERRBUFSIZE 100
static char errbuf[ERRBUFSIZE];
/*
* Fetch the address configuration data for all interfaces and push it into
* the pfil kernel module. Fetch the routing table, compute the valid address
* set data for all interfaces and push it into the pfil kernel module.
*/
static int
do_update(void)
{
int numifs, i;
struct pfil_ifaddrs *ifaddrlist;
const int lifc_flags = 0;
void *buf;
flag = 0;
return (-1);
}
return (-1);
}
return (-1);
}
/* Allocate memory for the number of interfaces retrieved. */
if (ifaddrlist == NULL) {
return (-1);
}
/* Populate the interface entries in the ifaddrlist. */
for (i = 0; i < numifs; i++) {
sizeof (ifaddrlist[i].localaddr));
"SIOCGLIFNETMASK %.*s: %s",
return (-1);
}
sizeof (ifaddrlist[i].netmask));
if (errno != EADDRNOTAVAIL) {
"SIOCGLIFBRDADDR %.*s: %s",
return (-1);
}
} else {
&lifrbuf[i].lifr_broadaddr,
sizeof (ifaddrlist[i].broadaddr));
}
if (errno != EADDRNOTAVAIL) {
"SIOCGLIFDSTADDR %.*s: %s",
return (-1);
}
} else {
&lifrbuf[i].lifr_dstaddr,
sizeof (ifaddrlist[i].dstaddr));
}
"SIOCGLIFDSTADDR %.*s: %s",
return (-1);
} else {
}
if (debuglevel > 0) {
ifaddrlist[i].mtu);
}
}
/*
* Now send this table of interfaces and addresses down into
* the pfil kernel module.
*/
if (pfil_msg(PFILCMD_IFADDRS,
ifaddrlist, i * sizeof (struct pfil_ifaddrs)) < 0) {
return (-1);
}
/*
* Next, compute and send the table of valid addresses.
*/
return (-1);
}
return (0);
}
/*
* Send an IPv6 packet out from the system using sendmsg on the raw IP socket
* through the ancillary data.
*/
static int
{
struct sockaddr_in6 sin6;
struct in6_pktinfo *pktinfop;
int fd;
pktinfop->ipi6_ifindex = 0;
sin6.sin6_scope_id = 0;
fd = icmp6_ip6_fd;
fd = tcp_ip6_fd;
else {
return (-1);
}
}
/*
* raw IP socket. Due to the awkwardness of the IPv6 socket API, IPv6 packets
* are limited to ICMP and TCP; other protocols are dropped.
*/
static void
{
int n;
if (debuglevel > 0) {
}
struct sockaddr_in sin;
} else {
n = -1;
}
if (n < 0)
perror("pfild: raw socket send");
}
{
exit(1);
}
int
{
int c, n;
const int on = 1;
int make_daemon = 1;
int pid;
struct icmp6_filter filter;
switch (c) {
case '?' :
break;
case 'd' :
make_daemon = 0;
debuglevel++;
break;
}
}
if (pfil_fd < 0) {
return (1);
}
if (ip_fd < 0) {
perror("pfild: inet socket");
return (1);
}
perror("pfild: inet socket IP_HDRINCL option");
return (1);
}
if (icmp6_ip6_fd < 0) {
perror("pfild: inet6 ICMP6 socket");
return (1);
}
/*
* ICMPv6 raw socket by default passes all ICMPv6 message received
* to the application. We don't care about them, so simply block them
* all.
*/
perror("pfild: inet6 ICMP6 socket type filtering option");
return (1);
}
if (tcp_ip6_fd < 0) {
perror("pfild: inet6 TCP socket");
return (1);
}
if (route_fd < 0) {
perror("pfild: socket(PF_ROUTE)");
return (1);
}
if (make_daemon) {
/* Background */
return (0);
if (pid < 0) {
return (1);
/* NOTREACHED */
}
(void) setsid();
(void) close(0);
(void) close(1);
(void) close(2);
(void) dup(0);
(void) dup(0);
(void) chdir("/");
}
/*
* Main loop: Poll for messages from PF_ROUTE socket or pfil stream.
* PF_ROUTE messages may indicate a need to update the kernel module's
* interface data. pfil messages contain packets to be transmitted.
* Errors in processing don't terminate the program, but errors in
* polling will terminate the program to avoid busy looping.
*/
while (1) {
if (flag) {
/* Wait for a moment of quiet, then do the update. */
if (do_update() != 0 && make_daemon == 0)
errbuf);
}
}
perror("pfild: poll()");
return (1);
}
/* Check for route_fd message. */
if (n < 1) {
if (n < 0)
perror("pfild: read(PF_ROUTE)");
else
"pfild: read(PF_ROUTE) EOF\n");
return (1);
}
}
/* Check for pfil_fd message. */
char pktbuf[IP_MAXPACKET];
int flags;
flags = 0;
if (n < 0) {
perror("pfild: getmsg(pfil)");
return (1);
}
if (n > 0) {
"pfild: invalid packet from kernel "
"n=%d ctl.len=%u data.len=%u\n",
return (1);
}
}
}
/* NOTREACHED */
}