xref: /netbsd-src/usr.sbin/btpand/tap.c (revision 309b0691a26d2cfb697a5af70986494c220d0294)
1 /*	$NetBSD: tap.c,v 1.5 2009/05/12 21:21:23 plunky Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 Iain Hibbert
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: tap.c,v 1.5 2009/05/12 21:21:23 plunky Exp $");
30 
31 #include <sys/ioctl.h>
32 #include <sys/uio.h>
33 
34 #include <net/if_dl.h>
35 #include <net/if_tap.h>
36 
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <util.h>
40 
41 #include "btpand.h"
42 
43 static void tap_exit(void);
44 static bool tap_send(channel_t *, packet_t *);
45 static bool tap_recv(packet_t *);
46 static void tap_down(channel_t *);
47 
48 void
tap_init(void)49 tap_init(void)
50 {
51 	channel_t *chan;
52 	struct sockaddr_dl *sdl;
53 	struct if_laddrreq iflr;
54 	struct ifreq ifr;
55 	int fd, s;
56 
57 	fd = open(interface_name, O_RDWR);
58 	if (fd == -1) {
59 		log_err("Could not open \"%s\": %m", interface_name);
60 		exit(EXIT_FAILURE);
61 	}
62 
63 	memset(&ifr, 0, sizeof(ifr));
64 	if (ioctl(fd, TAPGIFNAME, &ifr) == -1) {
65 		log_err("Could not get interface name: %m");
66 		exit(EXIT_FAILURE);
67 	}
68 	interface_name = strndup(ifr.ifr_name, IFNAMSIZ);
69 	atexit(tap_exit);
70 
71 	s = socket(PF_LINK, SOCK_DGRAM, 0);
72 	if (s == -1) {
73 		log_err("Could not open PF_LINK socket: %m");
74 		exit(EXIT_FAILURE);
75 	}
76 
77 	memset(&iflr, 0, sizeof(iflr));
78 	memcpy(iflr.iflr_name, ifr.ifr_name, IFNAMSIZ);
79 	iflr.flags = IFLR_ACTIVE;
80 
81 	sdl = satosdl(sstosa(&iflr.addr));
82 	sdl->sdl_family = AF_LINK;
83 	sdl->sdl_len = sizeof(struct sockaddr_dl);
84 	sdl->sdl_alen = ETHER_ADDR_LEN;
85 	b2eaddr(LLADDR(sdl), &local_bdaddr);
86 
87 	if (ioctl(s, SIOCALIFADDR, &iflr) == -1) {
88 		log_err("Could not add %s link address: %m", iflr.iflr_name);
89 		exit(EXIT_FAILURE);
90 	}
91 
92 	if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
93 		log_err("Could not get interface flags: %m");
94 		exit(EXIT_FAILURE);
95 	}
96 
97 	if ((ifr.ifr_flags & IFF_UP) == 0) {
98 		ifr.ifr_flags |= IFF_UP;
99 
100 		if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) {
101 			log_err("Could not set IFF_UP: %m");
102 			exit(EXIT_FAILURE);
103 		}
104 	}
105 
106 	close(s);
107 
108 	log_info("Using interface %s with addr %s",
109 	    ifr.ifr_name, ether_ntoa((struct ether_addr *)LLADDR(sdl)));
110 
111 	chan = channel_alloc();
112 	if (chan == NULL)
113 		exit(EXIT_FAILURE);
114 
115 	chan->send = tap_send;
116 	chan->recv = tap_recv;
117 	chan->down = tap_down;
118 	chan->mru = ETHER_HDR_LEN + ETHER_MAX_LEN;
119 	memcpy(chan->raddr, LLADDR(sdl), ETHER_ADDR_LEN);
120 	memcpy(chan->laddr, LLADDR(sdl), ETHER_ADDR_LEN);
121 	chan->state = CHANNEL_OPEN;
122 	if (!channel_open(chan, fd))
123 		exit(EXIT_FAILURE);
124 
125 	if (pidfile(ifr.ifr_name) == -1)
126 		log_err("pidfile not made");
127 }
128 
129 static void
tap_exit(void)130 tap_exit(void)
131 {
132 	struct ifreq ifr;
133 	int s;
134 
135 	s = socket(PF_LINK, SOCK_DGRAM, 0);
136 	if (s == -1) {
137 		log_err("Could not open PF_LINK socket: %m");
138 		return;
139 	}
140 
141 	strncpy(ifr.ifr_name, interface_name, IFNAMSIZ);
142 	if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
143 		log_err("Could not get interface flags: %m");
144 		return;
145 	}
146 
147 	if ((ifr.ifr_flags & IFF_UP)) {
148 		ifr.ifr_flags &= ~IFF_UP;
149 		if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) {
150 			log_err("Could not clear IFF_UP: %m");
151 			return;
152 		}
153 	}
154 
155 	close(s);
156 }
157 
158 static bool
tap_send(channel_t * chan,packet_t * pkt)159 tap_send(channel_t *chan, packet_t *pkt)
160 {
161 	struct iovec iov[4];
162 	ssize_t nw;
163 
164 	iov[0].iov_base = pkt->dst;
165 	iov[0].iov_len = ETHER_ADDR_LEN;
166 	iov[1].iov_base = pkt->src;
167 	iov[1].iov_len = ETHER_ADDR_LEN;
168 	iov[2].iov_base = pkt->type;
169 	iov[2].iov_len = ETHER_TYPE_LEN;
170 	iov[3].iov_base = pkt->ptr;
171 	iov[3].iov_len = pkt->len;
172 
173 	/* tap device write never fails */
174 	nw = writev(chan->fd, iov, __arraycount(iov));
175 	assert(nw > 0);
176 
177 	return true;
178 }
179 
180 static bool
tap_recv(packet_t * pkt)181 tap_recv(packet_t *pkt)
182 {
183 
184 	if (pkt->len < ETHER_HDR_LEN)
185 		return false;
186 
187 	pkt->dst = pkt->ptr;
188 	packet_adj(pkt, ETHER_ADDR_LEN);
189 	pkt->src = pkt->ptr;
190 	packet_adj(pkt, ETHER_ADDR_LEN);
191 	pkt->type = pkt->ptr;
192 	packet_adj(pkt, ETHER_TYPE_LEN);
193 
194 	return true;
195 }
196 
197 static void
tap_down(channel_t * chan)198 tap_down(channel_t *chan)
199 {
200 
201 	/* we never close the tap channel */
202 }
203