#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef _BPF_
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/uio.h>
#ifdef _LINUX_
#include <stdio.h>
#ifdef _GLIBC_
#include <net/if_packet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#else
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if.h>
#endif /*_GLIBC_*/
#else
#define MAX_IFS 32
#include <sys/time.h>
#include <net/bpf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/sysctl.h>
#include <net/route.h>
#include <netinet/if_ether.h>
#include <unistd.h>
#include "ip.h"
#ifdef _BSDRAW_
unsigned short in_cksum (unsigned short*,int);
#endif
#endif /*_LINUX_*/
#ifdef _BPF_
static int
get_ether_addr(u_long ipaddr, u_char *hwaddr)
{
struct ifreq *ifr, *ifend, *ifp;
u_long ina, mask;
struct sockaddr_dl *dla;
struct ifreq ifreq;
struct ifconf ifc;
struct ifreq ifs[MAX_IFS];
int s;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
perror("socket");
ifc.ifc_len = sizeof(ifs);
ifc.ifc_req = ifs;
if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
close(s);
return 0;
}
ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < ifend; ) {
if (ifr->ifr_addr.sa_family == AF_INET) {
ina = ((struct sockaddr_in *)
&ifr->ifr_addr)->sin_addr.s_addr;
strncpy(ifreq.ifr_name, ifr->ifr_name,
sizeof(ifreq.ifr_name));
if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
continue;
if ((ifreq.ifr_flags &
(IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
IFF_LOOPBACK|IFF_NOARP))
!= (IFF_UP|IFF_BROADCAST))
goto nextif;
if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
continue;
mask = ((struct sockaddr_in *)
&ifreq.ifr_addr)->sin_addr.s_addr;
if ((ipaddr & mask) != (ina & mask))
goto nextif;
break;
}
nextif:
ifr = (struct ifreq *)
((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
}
if (ifr >= ifend) {
close(s);
return 0;
}
ifp = ifr;
for (ifr = ifc.ifc_req; ifr < ifend; ) {
if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
&& ifr->ifr_addr.sa_family == AF_LINK) {
dla = (struct sockaddr_dl *) &ifr->ifr_addr;
memcpy(hwaddr,LLADDR(dla),dla->sdl_alen);
close (s);
return dla->sdl_alen;
}
ifr = (struct ifreq *)
((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
}
return 0;
}
#endif /*_BPF_*/
void send_eth_packet(int fd,char* eth_device,u_char *pkt,int len,int flag)
{
int retval;
#ifndef _BPF_
struct msghdr msg;
struct sockaddr_pkt spkt;
struct iovec iov;
strcpy(spkt.spkt_device, eth_device);
spkt.spkt_protocol = htons(ETH_P_IP);
memset(&msg, 0, sizeof(msg));
msg.msg_name = &spkt;
msg.msg_namelen = sizeof(spkt);
msg.msg_iovlen = 1;
msg.msg_iov = &iov;
iov.iov_base = pkt;
iov.iov_len = len;
retval = sendmsg(fd, &msg, 0);
#else
#ifdef _BSDRAW_
if(flag){
((struct iphdr *)(pkt + 14))->tot_len = htons(((struct iphdr *)(pkt + 14))->tot_len);
((struct iphdr *)(pkt + 14))->frag_off = htons(((struct iphdr *)(pkt + 14))->frag_off);
((struct iphdr *)(pkt + 14))->check = 0;
((struct iphdr *)(pkt + 14))->check = in_cksum((unsigned short*)(pkt + 14),
4*((struct iphdr *)(pkt + 14))->ihl);
}
#endif
retval = write(fd,pkt,len);
#endif
if (retval < 0) {
croak("send_eth_packet");
}
}
int mac_disc(unsigned int addr,unsigned char * eth_mac){
#ifndef _BPF_
struct arpreq
{
struct sockaddr arp_pa;
struct sockaddr arp_ha;
int arp_flags;
struct sockaddr arp_netmask;
char arp_dev[16];
} req;
int fd;
fd = socket(AF_INET,SOCK_DGRAM,0);
memset((char*)&req,0,sizeof(req));
req.arp_pa.sa_family = AF_INET;
*(unsigned int*)(req.arp_pa.sa_data+2) = htonl(addr);
if(ioctl(fd,SIOCGARP,&req) < 0){
close(fd);
return 0;
}
memcpy(eth_mac, req.arp_ha.sa_data, ETH_ALEN);
close(fd);
return 1;
#else
int mib[6],found;
size_t needed;
char *lim, *buf, *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin;
struct sockaddr_dl *sdl;
extern int h_errno;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
mib[5] = RTF_LLINFO;
found = 0;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
perror("route-sysctl-estimate");
if ((buf = (char*)malloc(needed)) == NULL)
perror("malloc");
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
perror("actual retrieval of routing table");
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
sin = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)(sin + 1);
if (addr != ntohl(sin->sin_addr.s_addr))
continue;
found = 1;
}
if(!found){
return 0;
} else {
memcpy(eth_mac,LLADDR(sdl),sdl->sdl_alen);
return 1;
}
#endif
}
int
tap(char *dev,unsigned int *my_eth_ip,unsigned char *my_eth_mac)
{
int fd,v,s;
struct ifreq ifr;
(void)strcpy(ifr.ifr_name, dev);
#ifndef _BPF_
if ((fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) {
croak("(tap) SOCK_PACKET allocation problems [fatal]");
}
#else
if ((fd = bpf_open()) < 0) croak("(tap) fd < 0");
v = 32768;
(void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
croak("(tap) BIOCSETIF problems [fatal]");
}
s = socket(AF_INET,SOCK_DGRAM,0);
if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
#endif
#ifndef _BPF_
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
close(fd);
croak("(tap) Can't get interface IP address");
#else
close(fd);
close(s);
#endif
croak("(tap) Can't get interface IP address");
}
*my_eth_ip = ntohl(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr.s_addr);
#ifndef _BPF_
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
close(fd);
croak("(tap) Can't get interface HW address");
}
memcpy(my_eth_mac, ifr.ifr_hwaddr.sa_data,ETH_ALEN);
#else
close(s);
if (!get_ether_addr(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr.s_addr,
my_eth_mac)) {
croak("(tap) Can't get interface HW address");
}
#endif
return fd;
}