1 /* -*- c++ -*- */
2 /*
3  * Copyright 2013 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "tuntap_pdu_impl.h"
28 #include <gnuradio/blocks/pdu.h>
29 #include <gnuradio/io_signature.h>
30 #include <boost/format.hpp>
31 
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 
36 #if (defined(linux) || defined(__linux) || defined(__linux__))
37 #include <arpa/inet.h>
38 #include <linux/if.h>
39 #include <sys/ioctl.h>
40 #endif
41 
42 namespace gr {
43 namespace blocks {
44 
make(std::string dev,int MTU,bool istunflag)45 tuntap_pdu::sptr tuntap_pdu::make(std::string dev, int MTU, bool istunflag)
46 {
47 #if (defined(linux) || defined(__linux) || defined(__linux__))
48     return gnuradio::get_initial_sptr(new tuntap_pdu_impl(dev, MTU, istunflag));
49 #else
50     throw std::runtime_error("tuntap_pdu not implemented on this platform");
51 #endif
52 }
53 
54 #if (defined(linux) || defined(__linux) || defined(__linux__))
tuntap_pdu_impl(std::string dev,int MTU,bool istunflag)55 tuntap_pdu_impl::tuntap_pdu_impl(std::string dev, int MTU, bool istunflag)
56     : block("tuntap_pdu", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)),
57       stream_pdu_base(istunflag ? MTU : MTU + 14),
58       d_dev(dev),
59       d_istunflag(istunflag)
60 {
61     // make the tuntap
62     char dev_cstr[1024];
63     memset(dev_cstr, 0x00, 1024);
64     strncpy(dev_cstr, dev.c_str(), std::min(sizeof(dev_cstr), dev.size()));
65 
66     bool istun = d_istunflag;
67     if (istun) {
68         d_fd = tun_alloc(dev_cstr, (IFF_TUN | IFF_NO_PI));
69     } else {
70         d_fd = tun_alloc(dev_cstr, (IFF_TAP | IFF_NO_PI));
71     }
72 
73     if (d_fd <= 0)
74         throw std::runtime_error(
75             "gr::tuntap_pdu::make: tun_alloc failed (are you running as root?)");
76 
77     int err = set_mtu(dev_cstr, MTU);
78     if (err < 0)
79         std::cerr << boost::format("gr::tuntap_pdu: failed to set MTU to %d.\n"
80                                    "You should use ifconfig to set the MTU. E.g.,\n"
81                                    "  $ sudo ifconfig %s mtu %d\n") %
82                          MTU % dev % MTU
83                   << std::endl;
84 
85     std::cout << boost::format("Allocated virtual ethernet interface: %s\n"
86                                "You must now use ifconfig to set its IP address. E.g.,\n"
87                                "  $ sudo ifconfig %s 192.168.200.1\n"
88                                "Be sure to use a different address in the same subnet "
89                                "for each machine.\n") %
90                      dev % dev
91               << std::endl;
92 
93     // set up output message port
94     message_port_register_out(pdu::pdu_port_id());
95     start_rxthread(this, pdu::pdu_port_id());
96 
97     // set up input message port
98     message_port_register_in(pdu::pdu_port_id());
99     set_msg_handler(pdu::pdu_port_id(), [this](pmt::pmt_t msg) { this->send(msg); });
100 }
101 
tun_alloc(char * dev,int flags)102 int tuntap_pdu_impl::tun_alloc(char* dev, int flags)
103 {
104     struct ifreq ifr;
105     int fd, err;
106     const char* clonedev = "/dev/net/tun";
107 
108     /* Arguments taken by the function:
109      *
110      * char *dev: the name of an interface (or '\0'). MUST have enough
111      *   space to hold the interface name if '\0' is passed
112      * int flags: interface flags (eg, IFF_TUN etc.)
113      */
114 
115     /* open the clone device */
116     if ((fd = open(clonedev, O_RDWR)) < 0)
117         return fd;
118 
119     /* preparation of the struct ifr, of type "struct ifreq" */
120     memset(&ifr, 0, sizeof(ifr));
121 
122     ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
123 
124     /* if a device name was specified, put it in the structure; otherwise,
125      * the kernel will try to allocate the "next" device of the
126      * specified type
127      */
128     if (*dev)
129         strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
130 
131     /* try to create the device */
132     if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0) {
133         close(fd);
134         return err;
135     }
136 
137     /* if the operation was successful, write back the name of the
138      * interface to the variable "dev", so the caller can know
139      * it. Note that the caller MUST reserve space in *dev (see calling
140      * code below)
141      */
142     strcpy(dev, ifr.ifr_name);
143 
144     /* this is the special file descriptor that the caller will use to talk
145      * with the virtual interface
146      */
147     return fd;
148 }
149 
set_mtu(const char * dev,int MTU)150 int tuntap_pdu_impl::set_mtu(const char* dev, int MTU)
151 {
152     struct ifreq ifr;
153     int sfd, err;
154 
155     /* MTU must be set by passing a socket fd to ioctl;
156      * create an arbitrary socket for this purpose
157      */
158     if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
159         return sfd;
160 
161     /* preparation of the struct ifr, of type "struct ifreq" */
162     memset(&ifr, 0, sizeof(ifr));
163     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
164     ifr.ifr_addr.sa_family = AF_INET; /* address family */
165     ifr.ifr_mtu = MTU;
166 
167     /* try to set MTU */
168     if ((err = ioctl(sfd, SIOCSIFMTU, (void*)&ifr)) < 0) {
169         close(sfd);
170         return err;
171     }
172 
173     close(sfd);
174     return MTU;
175 }
176 #endif
177 
178 } /* namespace blocks */
179 } /* namespace gr */
180