1 /*
2  *  $Id: write_ip.c,v 1.3 1999/06/24 18:35:34 dugsong Exp $
3  *
4  *  libnet
5  *  write_ip.c - writes a prebuilt IP packet to the network
6  *
7  *  Copyright (c) 1998, 1999 Mike D. Schiffman <mike@infonexus.com>
8  *                           route|daemon9 <route@infonexus.com>
9  *  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #if (HAVE_CONFIG_H)
35 #include "../include/config.h"
36 #endif
37 #include "../include/libnet.h"
38 
39 int
write_ip(int sock,u_char * buf,int len)40 write_ip(int sock, u_char *buf, int len)
41 {
42     int c;
43     struct sockaddr_in sin;
44     struct ip *ip_hdr;
45 
46 #if (RAW_IS_COOKED)
47     return write_ip_via_datalink(buf,len);
48 #endif /* RAW_IS_COOKED */
49 
50     ip_hdr = (struct ip *)buf;
51 
52 #if (BSD_BYTE_SWAP)
53     /*
54      *  For link access, we don't need to worry about the inconsistencies of
55      *  certain BSD kernels.  However, raw socket nuances abound.  Certain
56      *  BSD implmentations require the ip_len and ip_off fields to be in host
57      *  byte order.  It's MUCH easier to change it here than inside the bpf
58      *  writing routine.
59      */
60     ip_hdr->ip_len = FIX(ip_hdr->ip_len);
61     ip_hdr->ip_off = FIX(ip_hdr->ip_off);
62 #endif
63 
64     memset(&sin, 0, sizeof(struct sockaddr_in));
65     sin.sin_family      = AF_INET;
66     sin.sin_addr.s_addr = ip_hdr->ip_dst.s_addr;
67 
68     c = sendto(sock, buf, len, 0, (struct sockaddr *)&sin,
69 	       sizeof(struct sockaddr));
70     if (c != len)
71     {
72 #if (__DEBUG)
73         fprintf(stderr, "write_ip: %d bytes written (%s)\n", c, strerror(errno));
74 #endif
75     }
76 #if (BSD_BYTE_SWAP)
77     ip_hdr->ip_len = UNFIX(ip_hdr->ip_len);
78     ip_hdr->ip_off = UNFIX(ip_hdr->ip_off);
79 #endif
80 
81     return (c);
82 }
83 
84 #if (RAW_IS_COOKED)
85 /*
86  * write_ip_via_datalink
87  *
88  * The intent of this function is to provide an easy mechanism for someone
89  * to send an IP packet that is inserted into the network via the datalink
90  * access interfaces. This is useful in situations where raw sockets are
91  * cooked, which can prevent programs using write_ip from working on certain
92  * Unixes.
93  * To pull this off, we have to determine a source and destination MAC
94  * address for the packet, as well as the correct interface to send it on.
95  *
96  * This has currently only been implemented and tested on solaris 2.6/2.5.1.
97  */
98 
99 int
write_ip_via_datalink(u_char * buf,int len)100 write_ip_via_datalink(u_char *buf, int len)
101 {
102     char ebuf[1024];
103     const u_char *broadcast_hwaddr="\xff\xff\xff\xff\xff\xff";
104 
105     static u_long cached_dest_ip=0;
106     static u_char *cached_device=NULL;
107     static struct link_int *cached_linkent=NULL;
108 
109     struct link_int *linkent=NULL;
110     u_long local_ip;
111     u_long dest_ip;
112 
113     u_char *sbuf;
114     static u_char source_hwaddr[6];
115     static u_char dest_hwaddr[6];
116     u_long gateway_ip;
117     u_char *device;
118 
119     struct ip *ip_hdr;
120     int c;
121 
122     ip_hdr = (struct ip *)(buf);
123     dest_ip = ip_hdr->ip_dst.s_addr;
124 
125 #if (__DEBUG)
126     fprintf(stderr, "entering write_ip_via_datalink\n");
127 #endif
128 
129     if (cached_dest_ip == dest_ip)
130     {
131 #if (__DEBUG)
132         fprintf(stderr, "taking cached shortcut\n");
133 #endif
134         linkent=cached_linkent;
135         device=cached_device;
136         goto send_it;
137     }
138 
139     /*
140      * Step one: Pick the local interface to send the packet on.
141      *
142      * This requires support for the udp socket trick. If it isn't there
143      * then we will just pick the first available interface and try it out.
144      */
145 
146     local_ip=libnet_get_interface_by_dest(dest_ip,ebuf);
147 
148     if (local_ip==-1)
149     {
150         fprintf(stderr,"libnet_get_interface_by_dest: %s\n",ebuf);
151         return -1;
152     }
153     if (local_ip==0) /* We must be in Solaris 2.5.1 or lower. */
154     {
155         /*struct sockaddr_in tempsin;*/
156 
157 #if (__DEBUG)
158         fprintf(stderr, "libnet_get_interface_by_dest is not supported\n");
159 #endif
160         /* XXX - This function is currently broken under Solaris 2.5.1
161         if (libnet_select_device(&tempsin,&device,ebuf)==-1)
162         {
163             fprintf(stderr,"libnet_select_device: %s\n",ebuf);
164             return -1;
165         }
166         local_ip=tempsin.sin_addr.s_addr;*/
167 
168         /* Instead, we will try a couple of devices and see what happens. */
169 
170         if ((linkent=libnet_open_link_interface("le0",ebuf))==NULL)
171         {
172             if ((linkent=libnet_open_link_interface("hme0",ebuf))==NULL)
173             {
174                 fprintf(stderr,"I can't find the interface to use.\n");
175                 return -1;
176             }
177             else
178 		device="hme0";
179         }
180         else
181             device="le0";
182 
183         if ((local_ip=get_ipaddr(linkent,device,ebuf))==-1)
184         {
185             fprintf(stderr,"get_ipaddr: %s\n",ebuf);
186             return -1;
187         }
188     }
189     else
190     {
191         if (libnet_select_device_by_ip(&device,local_ip,ebuf)==-1)
192         {
193             fprintf(stderr,"libnet_select_device_by_ip: %s\n",ebuf);
194             return -1;
195         }
196     }
197 
198 #if (__DEBUG)
199     fprintf(stderr, "Using device %s with local IP of %s\n",device,
200             host_lookup(local_ip,0));
201 #endif
202 
203     /*
204      * Step two: Get the source MAC address.
205      */
206 
207     if (libnet_query_arp(local_ip,source_hwaddr,ebuf)==-1)
208     {
209         fprintf(stderr,"libnet_query_arp_cache: %s\n",ebuf);
210         return -1;
211     }
212 
213 #if (__DEBUG)
214     fprintf(stderr, "Using source MAC address %x:%x:%x:%x:%x:%x\n",
215             source_hwaddr[0], source_hwaddr[1], source_hwaddr[2],
216             source_hwaddr[3], source_hwaddr[4], source_hwaddr[5]);
217 #endif
218 
219     /*
220      * Step three: Get the destination MAC address.
221      *
222      * To really do this right, we use a routing socket to query the kernel
223      * for the appropriate gateway. If it is our own interface then we need
224      * to do an ARP resolution of the target IP. Otherwise, we need to do
225      * an ARP resolution of the gateway IP. If there is no support for routing
226      * sockets then we can use the broadcast MAC address which seems to work
227      * fine.
228      */
229 
230     if ((gateway_ip=libnet_get_next_hop(dest_ip, ebuf))==-1)
231     {
232 #if (__DEBUG)
233         fprintf(stderr, "libnet_get_next_hop failed: %s\n",ebuf);
234 #endif
235         memcpy(dest_hwaddr,broadcast_hwaddr,6);
236     }
237     else
238     {
239         if (gateway_ip==local_ip)
240         {
241             /* This is on our subnet. We need to request the ARP address of
242              * the destination IP. */
243             if (libnet_active_query_arp(dest_ip,dest_hwaddr,device,ebuf)==-1)
244             {
245 #if (__DEBUG)
246                 fprintf(stderr, "libnet_query_arp failed: %s\n",ebuf);
247 #endif
248                 memcpy(dest_hwaddr,broadcast_hwaddr,6);
249             }
250         }
251         else /* We need to send to a gateway. */
252         {
253             if (libnet_active_query_arp(gateway_ip,dest_hwaddr,device,ebuf)==-1)
254             {
255 #if (__DEBUG)
256                 fprintf(stderr, "libnet_query_arp failed: %s\n",ebuf);
257 #endif
258                 memcpy(dest_hwaddr,broadcast_hwaddr,6);
259             }
260         }
261     }
262 
263 #if (__DEBUG)
264     fprintf(stderr, "Using destination MAC address %x:%x:%x:%x:%x:%x\n",
265             dest_hwaddr[0], dest_hwaddr[1], dest_hwaddr[2],
266             dest_hwaddr[3], dest_hwaddr[4], dest_hwaddr[5]);
267 #endif
268 
269     /*
270      * Step four: ship it.
271      */
272 
273     if (!linkent && (linkent=libnet_open_link_interface(device,ebuf))==NULL)
274     {
275         fprintf(stderr,"libnet_open_link_interface: %s\n",ebuf);
276         return -1;
277     }
278 
279 send_it:
280 
281     if (libnet_init_packet(ETH_H + len, &sbuf)==-1)
282     {
283         fprintf(stderr,"libnet_init_packet: %s\n",ebuf);
284         return -1;
285     }
286 
287     libnet_build_ethernet(dest_hwaddr,source_hwaddr,ETHERTYPE_IP,NULL,0,sbuf);
288 
289     memcpy(sbuf+ETH_H,buf,len);
290     libnet_do_checksum(sbuf+ETH_H, IPPROTO_IP, ip_hdr->ip_hl*4);
291     if ((c=libnet_write_link_layer(linkent,device,sbuf,ETH_H+len))<0)
292     {
293         fprintf(stderr,"libnet_write_link_later: %s\n",strerror(errno));
294         return -1;
295     }
296 
297     cached_dest_ip=dest_ip;
298     cached_device=device;
299     if (cached_linkent && (cached_linkent != linkent))
300     {
301         libnet_close_link_interface(cached_linkent);
302     }
303     cached_linkent=linkent;
304 
305     libnet_destroy_packet(&sbuf);
306 
307     if (c != len +ETH_H)
308     {
309 #if (__DEBUG)
310         fprintf(stderr, "write_ip_via_datalink: %d bytes written (%s)\n", c, strerror(errno));
311 #endif
312     }
313 
314     return (c-ETH_H);
315 
316 }
317 #endif /* RAW_IS_COOKED */
318 
319 /* EOF */
320