2
0
This repository has been archived on 2025-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

437 lines
11 KiB
C

/*
* This software is Copyright 2011 by Sean Groarke <sgroarke@gmail.com>
* All rights reserved.
*
* This file is part of npd6.
*
* npd6 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* npd6 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with npd6. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Id$
* $HeadURL$
*/
#include "includes.h"
#include "npd6.h"
#include <netpacket/packet.h>
/*****************************************************************************
* open_packet_socket
* Opens the packet-level socket, for incoming traffic,
* and sets up the appropriate BSD PF.
*
* Inputs:
* Index of the interface we're opening it for.
*
* Outputs:
* none
*
* Return:
* int sock on success, otherwise -1
*
*/
int open_packet_socket(int ifIndex)
{
int sock, err;
struct sock_fprog fprog;
struct sockaddr_ll lladdr;
static const struct sock_filter filter[] =
{
BPF_STMT(BPF_LD|BPF_B|BPF_ABS,
ETH_HLEN +
sizeof(struct ip6_hdr) +
offsetof(struct icmp6_hdr, icmp6_type)),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ND_NEIGHBOR_SOLICIT, 1, 0),
BPF_STMT(BPF_RET|BPF_K, 0),
BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
};
fprog.filter = (struct sock_filter *)filter;
fprog.len = sizeof filter / sizeof filter[0];
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6) );
if (sock < 0)
{
flog(LOG_ERR, "Can't create socket(PF_PACKET): %s", strerror(errno));
return (-1);
}
flog(LOG_DEBUG2, "Created PF_PACKET socket OK.");
// Bind the socket to the interface we're interested in
memset(&lladdr, 0, sizeof(lladdr));
lladdr.sll_family = PF_PACKET;
lladdr.sll_protocol = htons(ETH_P_IPV6);
lladdr.sll_ifindex = ifIndex;
lladdr.sll_hatype = 0;
lladdr.sll_pkttype = 0;
lladdr.sll_halen = ETH_ALEN;
err=bind(sock, (struct sockaddr *)&lladdr, sizeof(lladdr));
if (err < 0)
{
flog(LOG_ERR, "packet socket bind to interface %d failed: %s", ifIndex, strerror(errno));
return (-1);
}
flog(LOG_DEBUG2, "packet socket bind to interface %d OK", ifIndex);
// Tie the BSD-PF filter to the socket
err = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (err < 0)
{
flog(LOG_ERR, "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
return (-1);
}
flog(LOG_DEBUG2, "setsockopt(SO_ATTACH_FILTER) OK");
return sock;
}
/*****************************************************************************
* open_icmpv6_socket
* Opens the ipv6-level socket, for outgoing traffic.
*
* Inputs:
* none
*
* Outputs:
* none
*
* Return:
* int sock on success, otherwise -1
*
*/
int open_icmpv6_socket(void)
{
int sock, err, optval = 1;
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (sock < 0)
{
flog(LOG_ERR, "Can't create socket(AF_INET6): %s", strerror(errno));
return (-1);
}
err = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &maxHops, sizeof(maxHops));
if (err < 0)
{
flog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS = %d): %s", maxHops, strerror(errno));
return (-1);
}
flog(LOG_DEBUG2, "setsockopt(IPV6_UNICAST_HOPS = %d) OK", maxHops);
/* Set packet info return via recvmsg() later */
/* Needed to get src addr of received ICMP pkts */
err = setsockopt(sock, IPPROTO_IPV6, IPV6_2292PKTINFO, &optval, sizeof(optval));
if (err < 0)
{
flog(LOG_ERR, "setsockopt(IPV6_2292PKTINFO): %s", strerror(errno));
return (-1);
}
flog(LOG_DEBUG2, "setsockopt(IPV6_2292PKTINFO) OK");
return sock;
}
/*****************************************************************************
* get_rx
* Called from the dispatcher to pull in the received packet.
*
* Inputs:
* socket is where the data is waiting.
*
* Outputs:
* unsigned char *msg
* The data.
*
* Return:
* int length of data received, otherwise -1 on error
*
* NOTES:
* There's a lot of temp data structures needed here, but we really don't
* care about them afterwards. Once we've got the raw data and the len
* we're good.
*/
int get_rx(int socket, unsigned char *msg)
{
struct sockaddr_in6 saddr;
struct msghdr mhdr;
struct iovec iov;
int len;
fd_set rfds;
FD_ZERO( &rfds );
FD_SET( socket, &rfds );
if( select( socket+1, &rfds, NULL, NULL, NULL ) < 0 )
{
if (errno != EINTR)
flog(LOG_ERR, "select failed with: %s", strerror(errno));
return -1;
}
iov.iov_len = MAX_MSG_SIZE;
iov.iov_base = (caddr_t) msg;
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)&saddr;
mhdr.msg_namelen = sizeof(saddr);
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = NULL;
mhdr.msg_controllen = 0;
len = recvmsg(socket, &mhdr, 0);
/* Impossible.. But let's not take chances */
if (len > MAX_MSG_SIZE)
{
flog(LOG_ERR, "Read more data from socket than we can handle. Ignoring it.");
return -1;
}
if (len < 0)
{
if (errno != EINTR)
flog(LOG_ERR, "recvmsg failed with: %s", strerror(errno));
return -1;
}
return len;
}
/*****************************************************************************
* get_rx_icmp6
* Called from the dispatcher to pull in the received packet.
*
* Inputs:
* socket is where the data is waiting.
*
* Outputs:
* unsigned char *msg
* The data.
*
* Return:
* int length of data received, otherwise -1 on error
*
* NOTES:
* There's a lot of temp data structures needed here, but we really don't
* care about them afterwards. Once we've got the raw data and the len
* we're good.
*/
int get_rx_icmp6(int socket, unsigned char *msg, struct in6_addr *addr6)
{
struct sockaddr_in6 saddr;
struct msghdr mhdr;
struct iovec iov[2];
int len;
fd_set rfds;
/* For ancillary data */
u_char cbuf[2048];
struct cmsghdr *cm;
struct in6_pktinfo *thispkt6;
char addr_str[INET6_ADDRSTRLEN];
FD_ZERO( &rfds );
FD_SET( socket, &rfds );
if( select( socket+1, &rfds, NULL, NULL, NULL ) < 0 )
{
if (errno != EINTR)
flog(LOG_ERR, "select failed with: %s", strerror(errno));
return -1;
}
iov[0].iov_len = MAX_MSG_SIZE;
iov[0].iov_base = (caddr_t) msg;
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = &saddr;
mhdr.msg_namelen = sizeof(saddr);
mhdr.msg_iov = iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = (caddr_t)cbuf;
mhdr.msg_controllen = sizeof(cbuf);
len = recvmsg(socket, &mhdr, 0);
/* Src addr - copy it for caller*/
memcpy(addr6, &(saddr.sin6_addr), sizeof(struct in6_addr));
print_addr(&(saddr.sin6_addr), addr_str);
flog( LOG_DEBUG2, "RA received src address: %s", addr_str);
/* Walk the chain of cmsg blocks (although unlikely to be more than 1) */
for (cm = CMSG_FIRSTHDR(&mhdr); cm != NULL; cm = CMSG_NXTHDR(&mhdr, cm))
{
if (cm->cmsg_level == IPPROTO_IPV6 &&
cm->cmsg_type == IPV6_2292PKTINFO &&
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
{
flog(LOG_DEBUG2, "Ancillary data contains in6_pktinfo.");
thispkt6 = (struct in6_pktinfo *)CMSG_DATA(cm);
// Dst addr
char addr_str[INET6_ADDRSTRLEN];
print_addr(&(thispkt6->ipi6_addr), addr_str);
flog( LOG_DEBUG2, "RA received dst address: %s", addr_str);
}
else
{
flog(LOG_DEBUG2, "Ancillary data was unrecognised.");
}
}
/* Impossible.. But let's not take chances */
if (len > MAX_MSG_SIZE)
{
flog(LOG_ERR, "Read more data from socket than we can handle. Ignoring it.");
return -1;
}
if (len < 0)
{
if (errno != EINTR)
flog(LOG_ERR, "recvmsg failed with: %s", strerror(errno));
return -1;
}
return len;
}
/*****************************************************************************
* if_allmulti
* Called during startup and shutdown. Set/clear allmulti
* as required.
*
* Inputs:
* ifname is interface name
* state: 1-> Set (or confirm) flag is enabled
* state: 0-> Set flag to unset condition.
*
* Outputs:
* none
*
* Return:
* The previous value of the flag, prior to change.
*
* Notes:
* Miserere mihi peccatori.
*/
int if_allmulti(char *ifname, unsigned int state)
{
struct ifreq ifr;
int skfd;
int current;
flog(LOG_DEBUG2, "Requesting that %s be set to state = %d",
ifname, state);
skfd = socket(AF_INET, SOCK_DGRAM, 0);
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
// Get current flags, etc.
if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
{
flog(LOG_ERR, "Unknown interface: %s, failed err = %s", ifname, strerror(errno));
exit(1);
}
current = ifr.ifr_flags;
if (state)
{
flog(LOG_DEBUG, "Setting IFFALLMULTI if required.");
ifr.ifr_flags |= IFF_ALLMULTI;
if (ifr.ifr_flags == current)
{
// Already set
flog(LOG_DEBUG, "Not required, was set at anyway.");
goto sinfulexit;;
}
}
else
{
flog(LOG_DEBUG, "Clearing IFFALLMULTI if required.");
ifr.ifr_flags &= ~IFF_ALLMULTI;
}
if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0)
{
flog(LOG_ERR, "Flag change failed: %s, failed err = %s", ifname, strerror(errno));
exit(1);
}
sinfulexit:
close(skfd);
return (current || IFF_ALLMULTI);
}
/*****************************************************************************
* init_sockets
* Initialises the tx and rx sockets. Normally just called during startup,
* but also to reinitialise sockets if they go bad.
*
* Inputs:
* void
*
* Outputs:
* Set global sockicmp and per i/f rx pkt socket
*
* Return:
* Non-0 if failure, else 0.
*/
int init_sockets(void)
{
int errcount = 0;
int loop, sock, sockicmp;
/* Raw socket for receiving NSs */
for (loop=0; loop < interfaceCount; loop++)
{
sock = open_packet_socket(interfaces[loop].index);
if (sock < 0)
{
flog(LOG_ERR, "open_packet_socket: failed on iteration %d", loop);
errcount++;
}
interfaces[loop].pktSock = sock;
flog(LOG_DEBUG, "open_packet_socket: %d OK.", loop);
flog(LOG_DEBUG2, "open_packet_socket value = %d", sock);
/* ICMPv6 socket for sending NAs */
sockicmp = open_icmpv6_socket();
if (sockicmp < 0)
{
flog(LOG_ERR, "open_icmpv6_socket: failed.");
errcount++;
}
flog(LOG_DEBUG, "open_icmpv6_socket: OK.");
interfaces[loop].icmpSock = sockicmp;
}
return errcount;
}