00001 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 00002 /* 00003 * Copyright (c) University of Washington 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation; 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 */ 00018 00019 #include <unistd.h> 00020 #include <string> 00021 #include <iostream> 00022 #include <iomanip> 00023 #include <sstream> 00024 #include <stdlib.h> 00025 #include <errno.h> 00026 00027 #include <sys/socket.h> 00028 #include <sys/un.h> 00029 #include <sys/ioctl.h> 00030 #include <net/ethernet.h> 00031 #include <net/if.h> 00032 #include <netinet/in.h> 00033 #include <netpacket/packet.h> 00034 #include <arpa/inet.h> 00035 00036 #include "emu-encode-decode.h" 00037 00038 #define EMU_MAGIC 65867 00039 00040 static int gVerbose = 0; 00041 00042 #define LOG(msg) \ 00043 if (gVerbose) \ 00044 { \ 00045 std::cout << __FUNCTION__ << "(): " << msg << std::endl; \ 00046 } 00047 00048 #define ABORT(msg, printErrno) \ 00049 std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \ 00050 if (printErrno) \ 00051 { \ 00052 std::cout << " errno = " << errno << " (" << strerror (errno) << ")" << std::endl; \ 00053 } \ 00054 exit (-1); 00055 00056 #define ABORT_IF(cond, msg, printErrno) \ 00057 if (cond) \ 00058 { \ 00059 ABORT(msg, printErrno); \ 00060 } 00061 00062 /** 00063 * \brief Send the socket file descriptor we created back to the emu device, 00064 * which will read it as soon as we're done. 00065 * 00066 * \param path The socket address information from the Unix socket we use 00067 * to send the created socket back to. 00068 * \param fd The socket we're going to send. 00069 */ 00070 static void 00071 SendSocket (const char *path, int fd) 00072 { 00073 // 00074 // Open a Unix (local interprocess) socket to call back to the emu net 00075 // device. 00076 // 00077 LOG ("Create Unix socket"); 00078 int sock = socket (PF_UNIX, SOCK_DGRAM, 0); 00079 ABORT_IF (sock == -1, "Unable to open socket", 1); 00080 00081 // 00082 // We have this string called path, which is really a hex representation 00083 // of the endpoint that the net device created. It used a forward encoding 00084 // method (EmuBufferToString) to take the sockaddr_un it made and passed 00085 // the resulting string to us. So we need to take the inverse method 00086 // (EmuStringToBuffer) and build the same sockaddr_un over here. 00087 // 00088 socklen_t clientAddrLen; 00089 struct sockaddr_un clientAddr; 00090 00091 LOG ("Decode address " << path); 00092 bool rc = ns3::EmuStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen); 00093 ABORT_IF (rc == false, "Unable to decode path", 0); 00094 00095 LOG ("Connect"); 00096 int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen); 00097 ABORT_IF (status == -1, "Unable to connect to emu device", 1); 00098 00099 LOG ("Connected"); 00100 00101 // 00102 // This is arcane enough that a few words are worthwhile to explain what's 00103 // going on here. 00104 // 00105 // The interesting information (the socket FD) is going to go back to the 00106 // emu net device as an integer of ancillary data. Ancillary data is bits 00107 // that are not a part a socket payload (out-of-band data). We're also 00108 // going to send one integer back. It's just initialized to a magic number 00109 // we use to make sure that the emu device is talking to the emu socket 00110 // creator and not some other creator process. 00111 // 00112 // The struct iovec below is part of a scatter-gather list. It describes a 00113 // buffer. In this case, it describes a buffer (an integer) containing the 00114 // data that we're going to send back to the emu net device (that magic 00115 // number). 00116 // 00117 struct iovec iov; 00118 uint32_t magic = EMU_MAGIC; 00119 iov.iov_base = &magic; 00120 iov.iov_len = sizeof(magic); 00121 00122 // 00123 // The CMSG macros you'll see below are used to create and access control 00124 // messages (which is another name for ancillary data). The ancillary 00125 // data is made up of pairs of struct cmsghdr structures and associated 00126 // data arrays. 00127 // 00128 // First, we're going to allocate a buffer on the stack to contain our 00129 // data array (that contains the socket). Sometimes you'll see this called 00130 // an "ancillary element" but the msghdr uses the control message termimology 00131 // so we call it "control." 00132 // 00133 size_t msg_size = sizeof(int); 00134 char control[CMSG_SPACE(msg_size)]; 00135 00136 // 00137 // There is a msghdr that is used to minimize the number of parameters 00138 // passed to sendmsg (which we will use to send our ancillary data). This 00139 // structure uses terminology corresponding to control messages, so you'll 00140 // see msg_control, which is the pointer to the ancillary data and controllen 00141 // which is the size of the ancillary data array. 00142 // 00143 // So, initialize the message header that describes our ancillary/control data 00144 // and point it to the control message/ancillary data we just allocated space 00145 // for. 00146 // 00147 struct msghdr msg; 00148 msg.msg_name = 0; 00149 msg.msg_namelen = 0; 00150 msg.msg_iov = &iov; 00151 msg.msg_iovlen = 1; 00152 msg.msg_control = control; 00153 msg.msg_controllen = sizeof (control); 00154 msg.msg_flags = 0; 00155 00156 // 00157 // A cmsghdr contains a length field that is the length of the header and 00158 // the data. It has a cmsg_level field corresponding to the originating 00159 // protocol. This takes values which are legal levels for getsockopt and 00160 // setsockopt (here SOL_SOCKET). We're going to use the SCM_RIGHTS type of 00161 // cmsg, that indicates that the ancillary data array contains access rights 00162 // that we are sending back to the emu net device. 00163 // 00164 // We have to put together the first (and only) cmsghdr that will describe 00165 // the whole package we're sending. 00166 // 00167 struct cmsghdr *cmsg; 00168 cmsg = CMSG_FIRSTHDR(&msg); 00169 cmsg->cmsg_level = SOL_SOCKET; 00170 cmsg->cmsg_type = SCM_RIGHTS; 00171 cmsg->cmsg_len = CMSG_LEN(msg_size); 00172 // 00173 // We also have to update the controllen in case other stuff is actually 00174 // in there we may not be aware of (due to macros). 00175 // 00176 msg.msg_controllen = cmsg->cmsg_len; 00177 00178 // 00179 // Finally, we get a pointer to the start of the ancillary data array and 00180 // put our file descriptor in. 00181 // 00182 int *fdptr = (int*) (CMSG_DATA(cmsg)); 00183 *fdptr = fd; // 00184 00185 // 00186 // Actually send the file descriptor back to the emulated net device. 00187 // 00188 ssize_t len = sendmsg(sock, &msg, 0); 00189 ABORT_IF (len == -1, "Could not send socket back to emu net device", 1); 00190 00191 LOG ("sendmsg complete"); 00192 } 00193 00194 int 00195 main (int argc, char *argv[]) 00196 { 00197 int c; 00198 char *path = NULL; 00199 00200 opterr = 0; 00201 00202 while ((c = getopt (argc, argv, "vp:")) != -1) 00203 { 00204 switch (c) 00205 { 00206 case 'v': 00207 gVerbose = true; 00208 break; 00209 case 'p': 00210 path = optarg; 00211 break; 00212 } 00213 } 00214 00215 // 00216 // This program is spawned by an emu net device running in a simulation. It 00217 // wants to create a raw socket as described below. We are going to do the 00218 // work here since we're running suid root. Once we create the raw socket, 00219 // we have to send it back to the emu net device. We do that over a Unix 00220 // (local interprocess) socket. The emu net device created a socket to 00221 // listen for our response on, and it is expected to have encoded the address 00222 // information as a string and to have passed that string as an argument to 00223 // us. We see it here as the "path" string. We can't do anything useful 00224 // unless we have that string. 00225 // 00226 ABORT_IF (path == NULL, "path is a required argument", 0); 00227 LOG ("Provided path is \"" << path << "\""); 00228 // 00229 // The whole reason for all of the hoops we went through to call out to this 00230 // program will pay off here. We created this program to run as suid root 00231 // in order to keep the main simulation program from having to be run with 00232 // root privileges. We need root privileges to be able to open a raw socket 00233 // though. So all of these hoops are to allow us to exeucte the following 00234 // single line of code: 00235 // 00236 LOG ("Creating raw socket"); 00237 int sock = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 00238 ABORT_IF (sock == -1, "CreateSocket(): Unable to open raw socket", 1); 00239 00240 // 00241 // Send the socket back to the emu net device so it can go about its business 00242 // 00243 SendSocket (path, sock); 00244 00245 return 0; 00246 }