2480 lines
72 KiB
C
2480 lines
72 KiB
C
/* -*- Mode:cc-mode; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/* vim: set ai sw=2 ts=2 et cindent cino={1s: */
|
|
/*
|
|
* Host Identity Protocol
|
|
* Copyright (c) 2004-2012 the Boeing Company
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* \file hip_esp.c
|
|
*
|
|
* \authors Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
*
|
|
* \brief User-mode HIP ESP implementation.
|
|
*
|
|
*/
|
|
#include <stdio.h> /* printf() */
|
|
#ifdef __WIN32__
|
|
#include <win32/types.h>
|
|
#include <io.h>
|
|
#include <winsock2.h>
|
|
#include <win32/ip.h>
|
|
#else /* __WIN32__ */
|
|
#include <sys/stat.h>
|
|
#include <unistd.h> /* write() */
|
|
#include <pthread.h> /* pthread_exit() */
|
|
#include <sys/time.h> /* gettimeofday() */
|
|
#include <sys/errno.h> /* errno, etc */
|
|
#ifdef __MACOSX__
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#endif /* __MACOSX__ */
|
|
#include <netinet/ip.h> /* struct ip */
|
|
#include <netinet/ip6.h> /* struct ip6_hdr */
|
|
#include <netinet/icmp6.h> /* struct icmp6_hdr */
|
|
#include <netinet/tcp.h> /* struct tcphdr */
|
|
#include <netinet/udp.h> /* struct udphdr */
|
|
#include <arpa/inet.h>
|
|
#ifndef __MACOSX__
|
|
#include <linux/types.h>
|
|
#endif /* __MACOSX__ */
|
|
#endif /* __WIN32__ */
|
|
#include <string.h> /* memset, etc */
|
|
#include <openssl/hmac.h> /* HMAC algorithms */
|
|
#include <openssl/sha.h> /* SHA1 algorithms */
|
|
#include <openssl/des.h> /* 3DES algorithms */
|
|
#include <openssl/rand.h> /* RAND_bytes() */
|
|
#include <hip/hip_types.h>
|
|
#include <hip/hip_funcs.h>
|
|
#include <hip/hip_usermode.h>
|
|
#include <hip/hip_sadb.h>
|
|
#include <hip/hip_globals.h>
|
|
#include <win32/checksum.h>
|
|
|
|
#ifdef HIP_VPLS
|
|
#include <utime.h>
|
|
#include <netinet/ether.h>
|
|
#include <hip/hip_cfg_api.h>
|
|
#include <hip/endbox_utils.h>
|
|
#endif /* HIP_VPLS */
|
|
|
|
/*
|
|
* Globals
|
|
*/
|
|
|
|
#ifdef HIP_VPLS
|
|
int touchHeartbeat;
|
|
#endif
|
|
|
|
#ifdef __WIN32__
|
|
HANDLE tapfd;
|
|
#else
|
|
int tapfd;
|
|
#endif
|
|
int readsp[2] = { 0,0 };
|
|
int s_esp, s_esp_udp, s_esp_udp_dg, s_esp6;
|
|
|
|
#ifdef __MACOSX__
|
|
extern char *logaddr(struct sockaddr *addr);
|
|
/* __MACOS__ uses raw IP output */
|
|
#ifndef RAW_IP_OUT
|
|
#define RAW_IP_OUT
|
|
#endif
|
|
#endif
|
|
|
|
__u32 g_tap_lsi;
|
|
__u64 g_tap_mac;
|
|
long g_read_usec;
|
|
|
|
#define BUFF_LEN 2000
|
|
#define HMAC_SHA_96_BITS 96 /* 12 bytes */
|
|
#define REPLAY_WIN_SIZE 64 /* 64 packets */
|
|
|
|
#define MULTIHOMING_LOSS_THRESHOLD 5
|
|
|
|
/* array of Ethernet addresses used by get_eth_addr() */
|
|
#define MAX_ETH_ADDRS 255
|
|
__u8 eth_addrs[6 * MAX_ETH_ADDRS]; /* must be initialized to random values */
|
|
|
|
|
|
/* Prototype of checksum function defined in hip_util.c */
|
|
__u16 checksum_udp_packet(__u8 *data,
|
|
struct sockaddr *src,
|
|
struct sockaddr *dst);
|
|
|
|
#ifdef __WIN32__
|
|
#define IS_EINTR_ERROR() (WSAGetLastError() == WSAEINTR)
|
|
#else
|
|
#define IS_EINTR_ERROR() (errno == EINTR)
|
|
#endif /* __WIN32__ */
|
|
|
|
/*
|
|
* Local function declarations
|
|
*/
|
|
void tunreader_shutdown();
|
|
int handle_nsol(__u8 *in, int len, __u8 *out,int *outlen,struct sockaddr *addr);
|
|
int handle_arp(__u8 *in, int len, __u8 *out, int *outlen,struct sockaddr *addr);
|
|
int hip_esp_encrypt(__u8 *in, int len, __u8 *out, int *outlen,
|
|
hip_sadb_entry *entry, struct timeval *now);
|
|
int hip_esp_decrypt(__u8 *in, int len, __u8 *out, int *offset, int *outlen,
|
|
hip_sadb_entry *entry, struct ip *iph, struct timeval *now);
|
|
|
|
__u16 rewrite_checksum(__u8 *data, __u16 magic);
|
|
void add_eth_header(__u8 *data, __u64 src, __u64 dst, __u32 type);
|
|
void add_ipv4_header(__u8 *data, __u32 src, __u32 dst, struct ip *old,
|
|
__u16 len, __u8 proto);
|
|
void add_ipv6_pseudo_header(__u8 *data, struct sockaddr *src,
|
|
struct sockaddr *dst, __u32 len, __u8 proto);
|
|
void add_ipv6_header(__u8 *data,
|
|
struct sockaddr *src,
|
|
struct sockaddr *dst,
|
|
struct ip6_hdr *old,
|
|
struct ip *old4,
|
|
__u16 len,
|
|
__u8 proto);
|
|
__u16 in_cksum(struct ip *iph);
|
|
__u64 get_eth_addr(int family, __u8 *addr);
|
|
|
|
void esp_start_base_exchange(struct sockaddr *lsi);
|
|
void esp_start_expire(__u32 spi);
|
|
void esp_receive_udp_hip_packet(char *buff, int len);
|
|
void esp_signal_loss(__u32 spi, __u32 loss, struct sockaddr *dst);
|
|
__u32 get_next_seqno(hip_sadb_entry *entry);
|
|
int esp_anti_replay_check_initial(hip_sadb_entry *entry, __u32 seqno,
|
|
__u32 *sequence_hi);
|
|
__u64 esp_update_anti_replay(hip_sadb_entry *entry, __u32 seqno, __u32 seqno_hi);
|
|
|
|
/* externals */
|
|
extern __u32 get_preferred_lsi(struct sockaddr *lsi);
|
|
extern int do_bcast();
|
|
extern int maxof(int num_args, ...);
|
|
|
|
void init_readsp()
|
|
{
|
|
if (readsp[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef __MACOSX__
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, PF_UNSPEC, readsp))
|
|
{
|
|
#else
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, readsp))
|
|
{
|
|
#endif
|
|
printf("sockpair() failed\n");
|
|
}
|
|
/* also initialize the Ethernet address table */
|
|
RAND_bytes(eth_addrs, sizeof(eth_addrs));
|
|
}
|
|
|
|
/*
|
|
* hip_esp_output()
|
|
*
|
|
* The ESP output thread. Reads ethernet packets from the socketpair
|
|
* connected to the TAP-Win32 interface, and performs necessary ESP
|
|
* encryption. Also handles ARP requests with artificial replies.
|
|
*/
|
|
#ifdef __WIN32__
|
|
void hip_esp_output(void *arg)
|
|
#else
|
|
void *hip_esp_output(void *arg)
|
|
#endif
|
|
{
|
|
int len, err, flags, raw_len, is_broadcast, s, offset = 0;
|
|
fd_set fd;
|
|
struct timeval timeout, now;
|
|
__u8 raw_buff[BUFF_LEN];
|
|
__u8 data[BUFF_LEN]; /* encrypted data buffer */
|
|
struct ip *iph;
|
|
|
|
#ifdef __WIN32__
|
|
DWORD lenin;
|
|
OVERLAPPED overlapped = { 0 };
|
|
#endif
|
|
struct ip6_hdr *ip6h;
|
|
static hip_sadb_entry *entry;
|
|
struct sockaddr_storage ss_lsi;
|
|
struct sockaddr *lsi = (struct sockaddr*)&ss_lsi;
|
|
#ifndef RAW_IP_OUT
|
|
sockaddr_list *l;
|
|
#endif /* RAW_IP_OUT */
|
|
#ifndef HIP_VPLS
|
|
__u32 lsi_ip;
|
|
#else
|
|
struct arp_hdr *arph;
|
|
time_t last_time, now_time;
|
|
int packet_count = 0;
|
|
#endif
|
|
#ifdef RAW_IP_OUT
|
|
int s_raw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
|
if (s_raw < 0)
|
|
{
|
|
printf("*** socket() error for raw socket in hip_esp_output\n");
|
|
}
|
|
flags = 1;
|
|
if (setsockopt(s_raw, IPPROTO_IP, IP_HDRINCL, (char *)&flags,
|
|
sizeof(flags)) < 0)
|
|
{
|
|
printf("*** setsockopt() error for raw socket in "
|
|
"hip_esp_output\n");
|
|
}
|
|
#endif /* RAW_IP_OUT */
|
|
|
|
init_readsp();
|
|
lsi->sa_family = AF_INET;
|
|
get_preferred_lsi(lsi);
|
|
g_tap_lsi = LSI4(lsi);
|
|
|
|
#ifdef HIP_VPLS
|
|
touchHeartbeat = 1;
|
|
last_time = time(NULL);
|
|
printf("hip_esp_output() thread (tid %u pid %d) started...\n",
|
|
(unsigned)pthread_self(), getpid());
|
|
#else
|
|
printf("hip_esp_output() thread started...\n");
|
|
#endif
|
|
while (g_state == 0)
|
|
{
|
|
/* periodic select loop */
|
|
gettimeofday(&now, NULL);
|
|
FD_ZERO(&fd);
|
|
FD_SET((unsigned)readsp[1], &fd);
|
|
#ifdef __MACOSX__
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
#else
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = g_read_usec;
|
|
#endif
|
|
#ifdef HIP_VPLS
|
|
endbox_periodic_heartbeat(&now_time, &last_time, &packet_count,
|
|
"output", touchHeartbeat);
|
|
endbox_check_hello_time(&now_time);
|
|
#endif
|
|
|
|
if ((err =
|
|
select(readsp[1] + 1, &fd, NULL, NULL,
|
|
&timeout)) < 0)
|
|
{
|
|
if (IS_EINTR_ERROR())
|
|
{
|
|
continue;
|
|
}
|
|
printf("hip_esp_output(): select() error\n");
|
|
}
|
|
else if (err == 0)
|
|
{
|
|
/* idle cycle */
|
|
continue;
|
|
}
|
|
|
|
/* output data on socket */
|
|
memset(raw_buff, 0, sizeof(raw_buff));
|
|
memset(data, 0, sizeof(data));
|
|
memset(lsi, 0, sizeof(struct sockaddr_storage));
|
|
|
|
#ifdef __WIN32__
|
|
if ((len =
|
|
recv(readsp[1], raw_buff, BUFF_LEN,
|
|
0)) == SOCKET_ERROR)
|
|
{
|
|
#else
|
|
if ((len = read(readsp[1], raw_buff, BUFF_LEN)) < 0)
|
|
{
|
|
#endif
|
|
if (IS_EINTR_ERROR())
|
|
{
|
|
continue;
|
|
}
|
|
printf("hip_esp_output(): read() failed: %s\n",
|
|
strerror(errno));
|
|
exit(0);
|
|
}
|
|
/*
|
|
* IPv4
|
|
*/
|
|
if ((raw_buff[12] == 0x08) && (raw_buff[13] == 0x00))
|
|
{
|
|
iph = (struct ip*) &raw_buff[14];
|
|
/* accept IPv4 traffic to 1.x.x.x here */
|
|
#ifdef HIP_VPLS
|
|
if (endbox_ipv4_packet_check(iph, lsi,
|
|
&packet_count) < 0)
|
|
{
|
|
continue;
|
|
}
|
|
is_broadcast = FALSE;
|
|
/* Right now multicast only goes through currently
|
|
* established tunnels.
|
|
*/
|
|
if (IN_MULTICAST(ntohl(iph->ip_dst.s_addr)) ||
|
|
(((ntohl(iph->ip_dst.s_addr)) & 0x000000FF) ==
|
|
0x000000FF))
|
|
{
|
|
|
|
#else /* HIP_VPLS */
|
|
|
|
#if defined(__BIG_ENDIAN__) || defined(__arm__)
|
|
if (((iph->ip_v) == IPVERSION) &&
|
|
((iph->ip_dst.s_addr >> 24 & 0xFF) != 0x01))
|
|
#else /* BIG_ENDIAN */
|
|
if (((iph->ip_v) == IPVERSION) &&
|
|
((iph->ip_dst.s_addr & 0xFF) != 0x01))
|
|
#endif /* BIG_ENDIAN */
|
|
{
|
|
continue;
|
|
}
|
|
lsi_ip = ntohl(iph->ip_dst.s_addr);
|
|
lsi->sa_family = AF_INET;
|
|
LSI4(lsi) = lsi_ip;
|
|
is_broadcast = FALSE;
|
|
/* broadcast packets */
|
|
if ((lsi_ip & 0x00FFFFFF) == 0x00FFFFFF)
|
|
{
|
|
if (!do_bcast())
|
|
{
|
|
continue;
|
|
}
|
|
#endif /* HIP_VPLS */
|
|
/* unicast the broadcast to each entry */
|
|
entry = hip_sadb_get_next(NULL);
|
|
is_broadcast = TRUE;
|
|
/* unicast packets */
|
|
}
|
|
else if (!(entry = hip_sadb_lookup_addr(lsi)))
|
|
{
|
|
#ifdef HIP_VPLS
|
|
if (!endbox_check_cert(lsi))
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
/* No SADB entry. Send ACQUIRE if we haven't
|
|
* already, i.e. a new lsi_entry was created */
|
|
if (buffer_packet(lsi, raw_buff,
|
|
len) == TRUE)
|
|
{
|
|
esp_start_base_exchange(lsi);
|
|
}
|
|
continue;
|
|
}
|
|
raw_len = len;
|
|
while (entry)
|
|
{
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
#if defined RAW_IP_OUT
|
|
offset = sizeof(struct ip);
|
|
#else
|
|
offset = 0;
|
|
#endif
|
|
if (check_esp_seqno_overflow(entry))
|
|
{
|
|
esp_start_expire(entry->spi);
|
|
}
|
|
err = hip_esp_encrypt(raw_buff,
|
|
raw_len,
|
|
&data[offset],
|
|
&len,
|
|
entry,
|
|
&now);
|
|
if (err < 0)
|
|
{
|
|
entry->dropped++;
|
|
}
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
if (err)
|
|
{
|
|
if (!is_broadcast)
|
|
{
|
|
break;
|
|
}
|
|
entry = hip_sadb_get_next(entry);
|
|
continue;
|
|
}
|
|
flags = 0;
|
|
#ifdef RAW_IP_OUT
|
|
/* Build IPv4 header and send out raw socket.
|
|
* Use this to override OS source address
|
|
* selection problems.
|
|
*/
|
|
add_ipv4_header(data,
|
|
ntohl(LSI4(&entry->src_addrs->addr)),
|
|
ntohl(LSI4( &entry->dst_addrs->addr)),
|
|
(struct ip*) &raw_buff[sizeof(struct eth_hdr)],
|
|
sizeof(struct ip) + len, IPPROTO_ESP);
|
|
#ifdef __MACOSX__
|
|
err = sendto(s_esp, data, sizeof(struct ip) + len, flags, 0, 0);
|
|
#else
|
|
err = sendto(s_raw, data, sizeof(struct ip) + len, flags,
|
|
SA(&entry->dst_addrs->addr),
|
|
SALEN(&entry->dst_addrs->addr));
|
|
#endif /* __MACOSX__ */
|
|
|
|
#else
|
|
if (entry->mode == 3)
|
|
{
|
|
s = s_esp_udp;
|
|
}
|
|
else if (entry->dst_addrs->addr.ss_family ==
|
|
AF_INET)
|
|
{
|
|
s = s_esp;
|
|
}
|
|
else
|
|
{
|
|
s = s_esp6;
|
|
}
|
|
err = sendto(s, data, len, flags,
|
|
SA(&entry->dst_addrs->addr),
|
|
SALEN(&entry->dst_addrs->addr));
|
|
#endif /* RAW_IP_OUT */
|
|
if (err < 0)
|
|
{
|
|
printf("hip_esp_output(): sendto() "
|
|
"failed: %s\n", strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
hip_sadb_inc_bytes(
|
|
entry,
|
|
sizeof(struct ip) +
|
|
err,
|
|
&now,
|
|
1);
|
|
}
|
|
#ifndef RAW_IP_OUT
|
|
/* multihoming: duplicate packets to multiple
|
|
* destination addresses */
|
|
for (l = entry->dst_addrs->next; l;
|
|
l = l->next)
|
|
{
|
|
err = sendto(s, data, len, flags,
|
|
SA(&l->addr),
|
|
SALEN(&l->addr));
|
|
if (err < 0)
|
|
{
|
|
printf(
|
|
"hip_esp_output(): sendto() "
|
|
"failed: %s\n",
|
|
strerror(errno));
|
|
}
|
|
}
|
|
#endif /* !RAW_IP_OUT */
|
|
/* broadcasts are unicast to each association */
|
|
if (!is_broadcast)
|
|
{
|
|
break;
|
|
}
|
|
entry = hip_sadb_get_next(entry);
|
|
} /* end while */
|
|
}
|
|
/*
|
|
* IPv6
|
|
*/
|
|
else if ((raw_buff[12] == 0x86) && (raw_buff[13] == 0xdd))
|
|
{
|
|
ip6h = (struct ip6_hdr*) &raw_buff[14];
|
|
/* accept IPv6 traffic to 2001:10::/28 here */
|
|
if ((ip6h->ip6_vfc & 0xF0) != 0x60)
|
|
{
|
|
continue;
|
|
}
|
|
/* Look for all-nodes multicast address */
|
|
if (IN6_IS_ADDR_MC_LINKLOCAL(&ip6h->ip6_dst) &&
|
|
(ip6h->ip6_nxt == IPPROTO_ICMPV6))
|
|
{
|
|
err = handle_nsol(raw_buff, len, data,&len,lsi);
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef __WIN32__
|
|
if (!WriteFile(tapfd, data, len, &lenin,
|
|
&overlapped))
|
|
{
|
|
printf( "hip_esp_output WriteFile() " \
|
|
"failed.\n");
|
|
}
|
|
#else
|
|
if (write(tapfd, data, len) < 0)
|
|
{
|
|
printf( "hip_esp_output write() " \
|
|
"failed.\n");
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
else if (!IS_HIT(&ip6h->ip6_dst))
|
|
{
|
|
continue;
|
|
}
|
|
/* HIT prefix */
|
|
lsi->sa_family = AF_INET6;
|
|
memcpy(SA2IP(lsi), &ip6h->ip6_dst, SAIPLEN(lsi));
|
|
if (!(entry = hip_sadb_lookup_addr(lsi)))
|
|
{
|
|
if (buffer_packet(lsi, raw_buff,
|
|
len) == TRUE)
|
|
{
|
|
esp_start_base_exchange(lsi);
|
|
}
|
|
continue;
|
|
}
|
|
raw_len = len;
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
if (check_esp_seqno_overflow(entry))
|
|
{
|
|
esp_start_expire(entry->spi);
|
|
}
|
|
err = hip_esp_encrypt(raw_buff, raw_len,
|
|
data, &len, entry, &now);
|
|
if (err < 0)
|
|
{
|
|
entry->dropped++;
|
|
}
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
flags = 0;
|
|
if (entry->mode == 3)
|
|
{
|
|
s = s_esp_udp;
|
|
}
|
|
else if (entry->dst_addrs->addr.ss_family ==
|
|
AF_INET)
|
|
{
|
|
s = s_esp;
|
|
}
|
|
else
|
|
{
|
|
s = s_esp6;
|
|
}
|
|
err = sendto( s, data, len, flags,
|
|
SA(&entry->dst_addrs->addr),
|
|
SALEN(&entry->dst_addrs->addr));
|
|
if (err < 0)
|
|
{
|
|
printf("hip_esp_output IPv6 sendto() failed:"
|
|
" %s\n",strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
hip_sadb_inc_bytes(entry,
|
|
sizeof(struct ip6_hdr) + err,
|
|
&now, 1);
|
|
}
|
|
/*
|
|
* ARP
|
|
*/
|
|
}
|
|
else if ((raw_buff[12] == 0x08) && (raw_buff[13] == 0x06))
|
|
{
|
|
#ifndef HIP_VPLS
|
|
/*
|
|
* printf("Raw buffer before handle_arp:\n");
|
|
* hex_print("\n\t", raw_buff, 60, 0);
|
|
* log_(NORM, "LSI into handle_arp: %0X\n",
|
|
* lsi->sa_data);
|
|
*/
|
|
err = handle_arp(raw_buff, len, data, &len, lsi);
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef __WIN32__
|
|
if (!WriteFile(tapfd, data, len, &lenin,
|
|
&overlapped))
|
|
{
|
|
printf("hip_esp_output WriteFile() failed.\n");
|
|
}
|
|
#else
|
|
if (write(tapfd, data, len) < 0)
|
|
{
|
|
printf("hip_esp_output write() failed.\n");
|
|
}
|
|
#endif /* __WIN32__ */
|
|
#else /* HIP_VPLS */
|
|
/* Is the ARP for one of our legacy nodes? */
|
|
arph = (struct arp_hdr*) &raw_buff[14];
|
|
if (endbox_arp_packet_check(arph, lsi,
|
|
&packet_count) < 0)
|
|
{
|
|
continue;
|
|
}
|
|
/* Why send an acquire during ARP? */
|
|
/* Trying to do layer two */
|
|
if (!(entry = hip_sadb_lookup_addr(lsi)))
|
|
{
|
|
if (!endbox_check_cert(lsi))
|
|
{
|
|
continue;
|
|
}
|
|
if (buffer_packet(lsi, raw_buff,
|
|
len) == TRUE)
|
|
{
|
|
esp_start_base_exchange(lsi);
|
|
}
|
|
}
|
|
else /* Need to send packet */
|
|
{
|
|
raw_len = len;
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
#ifdef RAW_IP_OUT
|
|
offset = sizeof(struct ip);
|
|
#else
|
|
offset = 0;
|
|
#endif
|
|
err = hip_esp_encrypt(raw_buff,
|
|
raw_len,
|
|
&data[offset],
|
|
&len,
|
|
entry,
|
|
&now);
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
if (err)
|
|
{
|
|
break;
|
|
}
|
|
flags = 0;
|
|
#ifdef RAW_IP_OUT
|
|
/* Build IPv4 header and send out raw socket.
|
|
* Use this to override OS source address
|
|
* selection problems.
|
|
*/
|
|
add_ipv4_header(data, ntohl(LSI4(&entry->src_addrs->addr)),
|
|
ntohl(LSI4(&entry->dst_addrs-> addr)),
|
|
(struct ip*) &raw_buff[sizeof(struct eth_hdr)],
|
|
sizeof(struct ip) + len, IPPROTO_ESP);
|
|
#ifdef __MACOSX__
|
|
err = sendto(s_esp, data, sizeof(struct ip) + len, flags, 0, 0);
|
|
#else
|
|
err = sendto(s_raw, data, sizeof(struct ip) + len, flags,
|
|
SA(&entry->dst_addrs->addr),
|
|
SALEN(&entry->dst_addrs->addr));
|
|
#endif /* __MACOSX__ */
|
|
|
|
#else
|
|
if (entry->mode == 3)
|
|
{
|
|
s = s_esp_udp;
|
|
}
|
|
else if (entry->dst_addrs->addr.ss_family ==
|
|
AF_INET)
|
|
{
|
|
s = s_esp;
|
|
}
|
|
else
|
|
{
|
|
s = s_esp6;
|
|
}
|
|
err = sendto(s, data, len, flags,
|
|
SA(&entry->dst_addrs->addr),
|
|
SALEN(&entry->dst_addrs->addr));
|
|
#endif /* RAW_IP_OUT */
|
|
if (err < 0)
|
|
{
|
|
printf("hip_esp_output(): sendto() "
|
|
"failed: %s\n", strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
entry->bytes += sizeof(struct ip) + err;
|
|
entry->usetime.tv_sec = now.tv_sec;
|
|
entry->usetime.tv_usec = now.tv_usec;
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
}
|
|
}
|
|
#endif /* HIP_VPLS */
|
|
continue;
|
|
/*
|
|
* Endbox hellos (uses protocol IEEE Std 802 - Local
|
|
* Experimental Ethertype 1).
|
|
*/
|
|
#ifdef HIP_VPLS
|
|
}
|
|
else if ((raw_buff[12] == 0x88) && (raw_buff[13] == 0xB5))
|
|
{
|
|
if (HCNF.endbox_hello_time > 0)
|
|
{
|
|
endbox_hello_check(raw_buff);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* debug other eth headers here */
|
|
/*int i;
|
|
* printf("<unknown traffic> (len=%d)\n", len);
|
|
* for (i = 0; i < len; i++)
|
|
* printf("%x", raw_buff[i] & 0xFF);
|
|
* printf("\n");*/
|
|
|
|
}
|
|
|
|
}
|
|
/* write some data to flush waiting TAP threads, speed up exit */
|
|
data[0] = 0;
|
|
len = 1;
|
|
#ifdef __WIN32__
|
|
WriteFile(tapfd, data, len, &lenin, &overlapped);
|
|
CloseHandle(tapfd);
|
|
#else
|
|
err = write(tapfd, data, len);
|
|
close(tapfd);
|
|
#endif
|
|
printf("hip_esp_output() thread shutdown.\n");
|
|
fflush(stdout);
|
|
tunreader_shutdown();
|
|
#ifndef __WIN32__
|
|
pthread_exit((void *) 0);
|
|
return(NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* hip_esp_input()
|
|
*
|
|
* The ESP input thread. Reads ESP packets from the network and decrypts
|
|
* them, adding HIT or LSI headers and sending them out the TAP-Win32 interface.
|
|
* Also, expires temporary LSI entries and retransmits buffered packets.
|
|
*/
|
|
#ifdef __WIN32__
|
|
void hip_esp_input(void *arg)
|
|
#else
|
|
void *hip_esp_input(void *arg)
|
|
#endif
|
|
{
|
|
int err, len, max_fd, offset;
|
|
fd_set fd;
|
|
struct timeval timeout, now;
|
|
__u8 buff[BUFF_LEN]; /* raw, encrypted data buffer */
|
|
__u8 data[BUFF_LEN]; /* decrypted data buffer */
|
|
struct sockaddr_storage ss_lsi;
|
|
struct sockaddr *lsi = (struct sockaddr*) &ss_lsi;
|
|
struct ip *iph;
|
|
struct ip_esp_hdr *esph;
|
|
udphdr *udph;
|
|
|
|
__u32 spi;
|
|
hip_sadb_entry *entry;
|
|
#ifdef __WIN32__
|
|
DWORD lenin;
|
|
OVERLAPPED overlapped = { 0 };
|
|
#endif
|
|
#ifdef HIP_VPLS
|
|
time_t last_time, now_time;
|
|
int packet_count = 0;
|
|
|
|
last_time = time(NULL);
|
|
printf("hip_esp_input() thread (tid %d pid %d) started...\n",
|
|
(unsigned)pthread_self(), getpid());
|
|
#else
|
|
printf("hip_esp_input() thread started...\n");
|
|
#endif
|
|
g_read_usec = 1000000;
|
|
|
|
lsi->sa_family = AF_INET;
|
|
get_preferred_lsi(lsi);
|
|
g_tap_lsi = LSI4(lsi);
|
|
|
|
while (g_state == 0)
|
|
{
|
|
gettimeofday(&now, NULL);
|
|
FD_ZERO(&fd);
|
|
FD_SET((unsigned)s_esp, &fd);
|
|
FD_SET((unsigned)s_esp_udp, &fd);
|
|
#ifdef __WIN32__
|
|
/* IPv6 ESP not available in Windows. Separate UDP datagram
|
|
* socket not needed. */
|
|
max_fd = (s_esp > s_esp_udp) ? s_esp : s_esp_udp;
|
|
#else
|
|
FD_SET((unsigned)s_esp_udp_dg, &fd);
|
|
#ifdef __MACOSX__
|
|
max_fd = maxof(3, s_esp, s_esp_udp, s_esp_udp_dg);
|
|
#else /* __MACOSX__ */
|
|
FD_SET((unsigned)s_esp6, &fd);
|
|
max_fd = maxof(4, s_esp, s_esp6, s_esp_udp, s_esp_udp_dg);
|
|
#endif /* __MACOSX__ */
|
|
#endif /* __WIN32__ */
|
|
#ifdef __MACOSX__
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
#else
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = g_read_usec;
|
|
#endif /* __MACOSX__ */
|
|
memset(buff, 0, sizeof(buff));
|
|
memset(data, 0, sizeof(data));
|
|
|
|
/* periodic functions called every g_read_usec timeout */
|
|
#ifdef HIP_VPLS
|
|
endbox_periodic_heartbeat(&now_time, &last_time, &packet_count,
|
|
"input", touchHeartbeat);
|
|
#endif
|
|
hip_remove_expired_lsi_entries(&now); /* unbuffer packets */
|
|
hip_remove_expired_sel_entries(&now); /* this is rate-limited */
|
|
hip_sadb_expire(&now);
|
|
|
|
if ((err =
|
|
select(max_fd + 1, &fd, NULL, NULL,
|
|
&timeout)) < 0)
|
|
{
|
|
if (IS_EINTR_ERROR())
|
|
{
|
|
continue;
|
|
}
|
|
printf("hip_esp_input(): select() error %s\n",
|
|
strerror(errno));
|
|
}
|
|
else if (FD_ISSET(s_esp, &fd))
|
|
{
|
|
#ifdef __WIN32__
|
|
len = recv(s_esp, buff, sizeof(buff), 0);
|
|
#else
|
|
len = read(s_esp, buff, sizeof(buff));
|
|
#endif
|
|
iph = (struct ip *) &buff[0];
|
|
esph = (struct ip_esp_hdr *) &buff[sizeof(struct ip)];
|
|
spi = ntohl(esph->spi);
|
|
if (!(entry = hip_sadb_lookup_spi(spi)))
|
|
{
|
|
/*printf("Warning: SA not found for SPI 0x%x\n",
|
|
* spi);*/
|
|
continue;
|
|
}
|
|
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
err = hip_esp_decrypt(buff, len, data, &offset, &len,
|
|
entry, iph, &now);
|
|
if (err < 0)
|
|
{
|
|
entry->dropped++;
|
|
}
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef __WIN32__
|
|
if (!WriteFile(tapfd, &data[offset], len, &lenin,
|
|
&overlapped))
|
|
{
|
|
printf("hip_esp_input() WriteFile() failed.\n");
|
|
continue;
|
|
}
|
|
#else /* __WIN32__ */
|
|
#ifdef HIP_VPLS
|
|
packet_count++;
|
|
iph =
|
|
(struct ip*) &data[offset +
|
|
sizeof(struct eth_hdr)];
|
|
endbox_ipv4_multicast_write(data, offset, len);
|
|
#else /* HIP_VPLS */
|
|
if (write(tapfd, &data[offset], len) < 0)
|
|
{
|
|
printf("hip_esp_input() write() failed.\n");
|
|
}
|
|
#endif /* HIP_VPLS */
|
|
#endif /* __WIN32__ */
|
|
}
|
|
else if (FD_ISSET(s_esp_udp, &fd))
|
|
{
|
|
#ifdef __WIN32__
|
|
len = recv(s_esp_udp, buff, sizeof(buff), 0);
|
|
#else
|
|
len = read(s_esp_udp, buff, sizeof(buff));
|
|
#endif /* __WIN32__ */
|
|
|
|
if (len < (sizeof(struct ip) + sizeof(udphdr)))
|
|
{
|
|
continue; /* packet too short */
|
|
}
|
|
iph = (struct ip*) &buff[0];
|
|
udph = (udphdr*) &buff[sizeof(struct ip)];
|
|
esph = (struct ip_esp_hdr *) \
|
|
&buff[sizeof(struct ip) + sizeof(udphdr)];
|
|
spi = ntohl(esph->spi);
|
|
/*seq_no = ntohl(esph->seq_no);*/
|
|
|
|
/* SOCK_RAW receives all UDP traffic, not just
|
|
* HIP_UDP_PORT, even though we used bind(). */
|
|
if (HIP_UDP_PORT != ntohs(udph->dst_port))
|
|
{
|
|
/* printf("ignoring %d bytes from UDP port
|
|
* %d\n",
|
|
* len, ntohs(udph->dst_port)); */
|
|
continue;
|
|
}
|
|
|
|
/* UDP packet with SPI of zero is a HIP control packet,
|
|
* send it to the hipd thread via ESP socketpair.
|
|
*/
|
|
if (0x0 == spi)
|
|
{
|
|
esp_receive_udp_hip_packet((char *)buff, len);
|
|
continue;
|
|
}
|
|
|
|
if (!(entry = hip_sadb_lookup_spi(spi)))
|
|
{
|
|
printf("Warning: SA not found for SPI 0x%x\n",
|
|
spi);
|
|
continue;
|
|
}
|
|
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
err = hip_esp_decrypt(buff, len, data, &offset, &len,
|
|
entry, iph, &now);
|
|
if (err < 0)
|
|
{
|
|
entry->dropped++;
|
|
}
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
/* these two locks acquired by hip_sadb_lookup_addr */
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifdef __WIN32__
|
|
if (!WriteFile(tapfd, &data[offset], len, &lenin,
|
|
&overlapped))
|
|
{
|
|
printf("hip_esp_input() WriteFile() failed.\n");
|
|
continue;
|
|
}
|
|
#else
|
|
if (write(tapfd, &data[offset], len) < 0)
|
|
{
|
|
printf("hip_esp_input() write() failed.\n");
|
|
}
|
|
#endif
|
|
|
|
#ifndef __WIN32__
|
|
}
|
|
else if (FD_ISSET(s_esp_udp_dg, &fd))
|
|
{
|
|
len = read(s_esp_udp_dg, buff, sizeof(buff));
|
|
/* This data is ignored, it was already received by the
|
|
* s_esp_udp RAW socket. This bound datagram socket
|
|
* prevents ICMP port unreachable messages. */
|
|
continue;
|
|
#ifndef __MACOSX__
|
|
}
|
|
else if (FD_ISSET(s_esp6, &fd))
|
|
{
|
|
len = read(s_esp6, buff, sizeof(buff));
|
|
/* there is no IPv6 header supplied */
|
|
esph = (struct ip_esp_hdr *) &buff[0];
|
|
spi = ntohl(esph->spi);
|
|
/* seq_no = ntohl(esph->seq_no);*/
|
|
if (!(entry = hip_sadb_lookup_spi(spi)))
|
|
{
|
|
printf("Warning: SA not found for SPI 0x%x\n",
|
|
spi);
|
|
continue;
|
|
}
|
|
pthread_mutex_lock(&entry->rw_lock);
|
|
err = hip_esp_decrypt(buff, len, data, &offset, &len,
|
|
entry, NULL, &now);
|
|
if (err < 0)
|
|
{
|
|
entry->dropped++;
|
|
}
|
|
pthread_mutex_unlock(&entry->rw_lock);
|
|
if (err)
|
|
{
|
|
continue;
|
|
}
|
|
if (write(tapfd, &data[offset], len) < 0)
|
|
{
|
|
printf("hip_esp_input() write() failed.\n");
|
|
}
|
|
#endif /* !__MACOSX__ */
|
|
#endif /* !__WIN32__ */
|
|
}
|
|
else if (err == 0)
|
|
{
|
|
/* idle cycle */
|
|
}
|
|
}
|
|
|
|
printf("hip_esp_input() thread shutdown.\n");
|
|
fflush(stdout);
|
|
#ifndef __WIN32__
|
|
pthread_exit((void *) 0);
|
|
return(NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef __WIN32__
|
|
/* For Windows, use overlapped event notification */
|
|
void tunreader(void *arg)
|
|
{
|
|
DWORD len;
|
|
char buf[BUFF_LEN];
|
|
OVERLAPPED overlapped;
|
|
int status;
|
|
|
|
printf("tunreader() thread started...\n");
|
|
|
|
init_readsp();
|
|
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
while (g_state == 0)
|
|
{
|
|
overlapped.Offset = 0;
|
|
overlapped.OffsetHigh = 0;
|
|
ResetEvent(overlapped.hEvent);
|
|
|
|
status = ReadFile(tapfd, buf, BUFF_LEN, &len, &overlapped);
|
|
if (!status)
|
|
{
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
{
|
|
/* WaitForSingleObject(overlapped.hEvent,2000);
|
|
*/
|
|
WaitForSingleObject(overlapped.hEvent,INFINITE);
|
|
if (!GetOverlappedResult(tapfd, &overlapped,
|
|
&len, FALSE))
|
|
{
|
|
/* there is nothing to send */
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* other error, don't exit */
|
|
printf("tunreader(): error (%d) reading from ",
|
|
(int)GetLastError());
|
|
printf("tun device.\n");
|
|
continue;
|
|
}
|
|
}
|
|
send(readsp[0], buf, len, 0);
|
|
}
|
|
CloseHandle(tapfd);
|
|
printf("tunreader() thread shutdown.\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
#else /* __WIN32__ */
|
|
|
|
/* For Linux, use select. */
|
|
void *tunreader(void *arg)
|
|
{
|
|
int len, err;
|
|
char buf[BUFF_LEN];
|
|
struct timeval timeout;
|
|
fd_set read_fdset;
|
|
|
|
#ifdef HIP_VPLS
|
|
time_t last_time, last_hello_time, now_time;
|
|
|
|
last_time = time(NULL);
|
|
last_hello_time = time(NULL);
|
|
printf("tunreader() thread (tid %d pid %d) started (%d)...\n",
|
|
(unsigned)pthread_self(), getpid(), tapfd);
|
|
#else
|
|
printf("tunreader() thread started (%d)...\n", tapfd);
|
|
#endif
|
|
|
|
init_readsp();
|
|
while (g_state == 0)
|
|
{
|
|
FD_ZERO(&read_fdset);
|
|
FD_SET((unsigned)tapfd, &read_fdset);
|
|
timeout.tv_sec = 3;
|
|
timeout.tv_usec = 0;
|
|
#ifdef HIP_VPLS
|
|
now_time = time(NULL);
|
|
if (now_time - last_time > 60)
|
|
{
|
|
printf("tunreader() heartbeat\n");
|
|
last_time = now_time;
|
|
utime("/usr/local/etc/hip/heartbeat_tunreader", NULL);
|
|
}
|
|
if ((HCNF.endbox_hello_time > 0) &&
|
|
(now_time - last_hello_time > HCNF.endbox_hello_time))
|
|
{
|
|
last_hello_time = now_time;
|
|
endbox_send_hello();
|
|
}
|
|
#endif
|
|
if ((err = select((tapfd + 1), &read_fdset,
|
|
NULL, NULL, &timeout) < 0))
|
|
{
|
|
if (err == EINTR)
|
|
{
|
|
continue;
|
|
}
|
|
printf("tunreader: error while reading from tun ");
|
|
printf("device: %s\n", strerror(errno));
|
|
fflush(stdout);
|
|
return(0);
|
|
}
|
|
else if (FD_ISSET(tapfd, &read_fdset))
|
|
{
|
|
if ((len = read(tapfd, buf, BUFF_LEN)) > 0)
|
|
{
|
|
err = write(readsp[0], buf, len);
|
|
if (err != len)
|
|
{
|
|
printf("warning: tunreader: write(%d) "
|
|
"returned %d\n", len, err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("tunreader: read() error len=%d %s\n",
|
|
len, strerror(errno));
|
|
continue;
|
|
}
|
|
}
|
|
else if (err == 0)
|
|
{
|
|
/* idle cycle */
|
|
continue;
|
|
}
|
|
}
|
|
close(tapfd);
|
|
printf("tunreader thread shutdown.\n");
|
|
fflush(stdout);
|
|
pthread_exit((void *) 0);
|
|
return(NULL);
|
|
}
|
|
|
|
#endif /* __WIN32__ */
|
|
|
|
/*
|
|
* tunreader_shutdown()
|
|
*
|
|
* Send dummy data to the tun device so that the tunreader() thread doesn't
|
|
* hang waiting for a read event.
|
|
*/
|
|
void tunreader_shutdown()
|
|
{
|
|
char data[8] = { 0,0,0,0,0,0,0,0 };
|
|
struct sockaddr_in to;
|
|
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
to.sin_family = AF_INET;
|
|
to.sin_addr.s_addr = htonl(g_tap_lsi);
|
|
to.sin_port = htons(8000);
|
|
|
|
sendto(s, data, sizeof(data), 0, (struct sockaddr*)&to, sizeof(to));
|
|
#ifdef __WIN32__
|
|
closesocket(s);
|
|
#else
|
|
close(s);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* handle_nsol()
|
|
*
|
|
* Handle ICMPv6 Neighbor Solicitations for HITs.
|
|
* Right now this is called from the esp_output thread when an
|
|
* application wants to send data to a HIT.
|
|
*/
|
|
int handle_nsol(__u8 *in, int len, __u8 *out, int *outlen,struct sockaddr *addr)
|
|
{
|
|
struct eth_hdr *eth = (struct eth_hdr*)in;
|
|
struct ip6_hdr *ip6h = (struct ip6_hdr*) &in[sizeof(struct eth_hdr)];
|
|
__u64 esrc = 0, edst = 0;
|
|
struct icmp6_hdr *nsol, *nadv;
|
|
struct in6_addr *target, *adv_target;
|
|
struct nd_opt_hdr *adv_target_opts;
|
|
__u8 *p;
|
|
__u16 payload_len;
|
|
int location;
|
|
struct sockaddr_storage src_ss;
|
|
struct sockaddr_storage dst_ss;
|
|
struct sockaddr *src = (struct sockaddr *) &src_ss;
|
|
struct sockaddr *dst = (struct sockaddr *) &dst_ss;
|
|
|
|
nsol = (struct icmp6_hdr *)&in[sizeof(struct eth_hdr) +
|
|
sizeof(struct ip6_hdr)];
|
|
|
|
/* Only allow ICMPv6 Neighbor Soliciations for HITs */
|
|
if (nsol->icmp6_type != ND_NEIGHBOR_SOLICIT)
|
|
{
|
|
return(1);
|
|
}
|
|
target = (struct in6_addr*) (nsol + 1);
|
|
if (!IS_HIT(target)) /* target must be HIT */
|
|
{
|
|
return(1);
|
|
}
|
|
/* don't answer requests for self */
|
|
src->sa_family = AF_INET6;
|
|
get_preferred_lsi(src);
|
|
if (IN6_ARE_ADDR_EQUAL(target,
|
|
&((struct sockaddr_in6*)src)->sin6_addr))
|
|
{
|
|
return(1);
|
|
}
|
|
|
|
/* for now, replied MAC addr */
|
|
esrc = get_eth_addr(AF_INET6, &target->s6_addr[0]);
|
|
memcpy(&edst, eth->src, 6);
|
|
add_eth_header(out, esrc, edst, 0x86dd);
|
|
location = sizeof(struct eth_hdr);
|
|
|
|
/* IPv6 header added after length is calculated */
|
|
memset(src, 0, sizeof(struct sockaddr_storage));
|
|
memset(dst, 0, sizeof(struct sockaddr_storage));
|
|
src->sa_family = AF_INET6;
|
|
memcpy(SA2IP(src), &target->s6_addr[0], sizeof(struct in6_addr));
|
|
dst->sa_family = AF_INET6;
|
|
memcpy(SA2IP(dst), &ip6h->ip6_src.s6_addr[0], sizeof(struct in6_addr));
|
|
location += sizeof(struct ip6_hdr);
|
|
|
|
/* build neighbor advertisement reply */
|
|
nadv = (struct icmp6_hdr *)&out[location];
|
|
nadv->icmp6_type = ND_NEIGHBOR_ADVERT;
|
|
nadv->icmp6_code = 0;
|
|
nadv->icmp6_cksum = 0;
|
|
nadv->icmp6_data32[0] = ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE;
|
|
location += sizeof(struct icmp6_hdr);
|
|
adv_target = (struct in6_addr*) &out[location];
|
|
memcpy(adv_target, target, sizeof(struct in6_addr));
|
|
location += sizeof(struct in6_addr);
|
|
adv_target_opts = (struct nd_opt_hdr*) &out[location];
|
|
adv_target_opts->nd_opt_type = ND_OPT_TARGET_LINKADDR;
|
|
adv_target_opts->nd_opt_len = 1; /* 1x(8 octets) */
|
|
location += sizeof(struct nd_opt_hdr);
|
|
memcpy(&out[location], &esrc, 6);
|
|
location += 6;
|
|
|
|
/* return the HIT */
|
|
if (addr)
|
|
{
|
|
memcpy(addr, src, sizeof(struct sockaddr_storage));
|
|
}
|
|
|
|
/* pseudo-header for upper-layer checksum calculation */
|
|
p = (__u8*)nadv - 40;
|
|
payload_len = &out[location] - (__u8*)nadv;
|
|
add_ipv6_pseudo_header(p, src, dst, (__u32)payload_len, IPPROTO_ICMPV6);
|
|
nadv->icmp6_cksum = ip_fast_csum(p, &out[location] - p);
|
|
/* real IPv6 header */
|
|
add_ipv6_header(&out[sizeof(struct eth_hdr)], src, dst, ip6h, NULL,
|
|
payload_len, IPPROTO_ICMPV6);
|
|
|
|
*outlen = location;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* handle_arp()
|
|
*
|
|
* Handle ARP requests for 1.x.x.x addresses. Right now this is called
|
|
* from the esp_output thread when an application wants to send data to
|
|
* an LSI.
|
|
*/
|
|
int handle_arp(__u8 *in, int len, __u8 *out, int *outlen, struct sockaddr *addr)
|
|
{
|
|
struct eth_hdr *eth = (struct eth_hdr*)in;
|
|
struct arp_hdr *arp_req_hdr, *arp_reply_hdr;
|
|
struct arp_req_data *arp_req, *arp_rply;
|
|
__u64 src = 0, dst = 0;
|
|
__u32 ip_dst;
|
|
|
|
/* only handle ARP requests (opcode 1) here */
|
|
arp_req_hdr = (struct arp_hdr*) &in[14];
|
|
switch (ntohs(arp_req_hdr->ar_op))
|
|
{
|
|
case ARPOP_REQUEST:
|
|
break;
|
|
default:
|
|
return(1);
|
|
}
|
|
|
|
if ((ntohs(arp_req_hdr->ar_hrd) == 0x01) && /* Ethernet */
|
|
(ntohs(arp_req_hdr->ar_pro) == 0x0800) && /* IPv4 */
|
|
(arp_req_hdr->ar_hln == 6) && (arp_req_hdr->ar_pln == 4))
|
|
{
|
|
/* skip sender MAC, sender IP, target MAC */
|
|
arp_req = (struct arp_req_data*)(arp_req_hdr + 1);
|
|
ip_dst = arp_req->dst_ip;
|
|
}
|
|
else
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
if (ip_dst == g_tap_lsi) /* don't answer requests for self */
|
|
{
|
|
return(1);
|
|
}
|
|
|
|
|
|
/* repl with random MAC addr based on requested IP addr */
|
|
src = get_eth_addr(AF_INET, (__u8*)&ip_dst);
|
|
/* log_(NORM, "Source MAC for reply: %08X\n", src); */
|
|
memcpy(&dst, eth->src, 6);
|
|
/* log_(NORM, "Dest MAC for reply: %08X\n", dst); */
|
|
add_eth_header(out, src, dst, 0x0806);
|
|
|
|
/* build ARP reply */
|
|
arp_reply_hdr = (struct arp_hdr*) &out[14];
|
|
arp_reply_hdr->ar_hrd = htons(0x01);
|
|
arp_reply_hdr->ar_pro = htons(0x0800);
|
|
arp_reply_hdr->ar_hln = 6;
|
|
arp_reply_hdr->ar_pln = 4;
|
|
arp_reply_hdr->ar_op = htons(ARPOP_REPLY);
|
|
arp_rply = (struct arp_req_data*)(arp_reply_hdr + 1);
|
|
memcpy(arp_rply->src_mac, &src, 6); /* sender MAC */
|
|
arp_rply->src_ip = arp_req->dst_ip; /* sender address */
|
|
memcpy(arp_rply->dst_mac, arp_req->src_mac, 6); /* target MAC */
|
|
arp_rply->dst_ip = arp_req->src_ip; /* target IP */
|
|
|
|
/*
|
|
* printf("Raw buffer arp_reply:\n");
|
|
* hex_print("\n\t", out, 60, 0);
|
|
*/
|
|
/* return the address */
|
|
if (addr)
|
|
{
|
|
addr->sa_family = AF_INET;
|
|
((struct sockaddr_in*)addr)->sin_addr.s_addr = ntohl(ip_dst);
|
|
}
|
|
|
|
*outlen = sizeof(struct eth_hdr) + sizeof(struct arp_hdr) + 20;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* hip_esp_encrypt()
|
|
*
|
|
* in: in pointer of data to encrypt
|
|
* len length of data
|
|
* out pointer of where to store encrypted data
|
|
* outlen returned length of encrypted data
|
|
* entry the SADB entry
|
|
*
|
|
* out: Encrypted data in out, outlen. entry statistics are modified.
|
|
* Returns 0 on success, -1 otherwise.
|
|
*
|
|
* Perform actual ESP encryption and authentication of packets.
|
|
*/
|
|
int hip_esp_encrypt(__u8 *in, int len, __u8 *out, int *outlen,
|
|
hip_sadb_entry *entry, struct timeval *now)
|
|
{
|
|
int alen = 0, elen = 0;
|
|
unsigned int hmac_md_len;
|
|
int i, iv_len = 0, padlen, location, hdr_len;
|
|
struct ip *iph = NULL;
|
|
struct ip6_hdr *ip6h = NULL;
|
|
struct ip_esp_hdr *esp;
|
|
udphdr *udph = NULL;
|
|
|
|
struct ip_esp_padinfo *padinfo = 0;
|
|
__u8 cbc_iv[16];
|
|
__u8 hmac_md[EVP_MAX_MD_SIZE];
|
|
#ifndef HIP_VPLS
|
|
__u16 checksum_fix = 0;
|
|
#endif
|
|
int family, use_udp = FALSE;
|
|
|
|
|
|
#ifndef HIP_VPLS
|
|
if ((in[12] == 0x86) && (in[13] == 0xdd))
|
|
{
|
|
family = AF_INET6;
|
|
}
|
|
else
|
|
{
|
|
family = AF_INET;
|
|
}
|
|
#else
|
|
family = AF_UNSPEC;
|
|
#endif
|
|
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
iph = (struct ip*) &in[sizeof(struct eth_hdr)];
|
|
/* BEET mode uses transport mode encapsulation. IP header is
|
|
* not included. */
|
|
hdr_len = sizeof(struct eth_hdr) + sizeof(struct ip);
|
|
/* rewrite upper-layer checksum, so it is based on HITs */
|
|
#ifndef HIP_VPLS
|
|
checksum_fix =
|
|
#endif
|
|
rewrite_checksum((__u8*)iph, entry->hit_magic);
|
|
break;
|
|
case AF_INET6:
|
|
ip6h = (struct ip6_hdr*) &in[sizeof(struct eth_hdr)];
|
|
hdr_len = sizeof(struct eth_hdr) + sizeof(struct ip6_hdr);
|
|
/* assume HITs are used as v6 src/dst, no checksum rewrite */
|
|
break;
|
|
#ifdef HIP_VPLS
|
|
case AF_UNSPEC:
|
|
hdr_len = 0;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
/* elen is length of data to encrypt
|
|
* for HIP_VPLS, this includes the IP header.
|
|
*/
|
|
elen = len - hdr_len;
|
|
|
|
|
|
/* setup ESP header, common to all algorithms */
|
|
if (entry->mode == 3) /*(HIP_ESP_OVER_UDP)*/
|
|
{
|
|
udph = (udphdr*) out;
|
|
esp = (struct ip_esp_hdr*) &out[sizeof(udphdr)];
|
|
use_udp = TRUE;
|
|
}
|
|
else
|
|
{
|
|
esp = (struct ip_esp_hdr*) out;
|
|
}
|
|
esp->spi = htonl(entry->spi);
|
|
esp->seq_no = htonl(get_next_seqno(entry));
|
|
padlen = 0;
|
|
*outlen = sizeof(struct ip_esp_hdr);
|
|
|
|
if (use_udp) /* (HIP_ESP_OVER_UDP) */
|
|
{
|
|
*outlen += sizeof(udphdr);
|
|
}
|
|
|
|
/*
|
|
* Encryption
|
|
*/
|
|
|
|
/* Check keys and set IV length */
|
|
switch (entry->e_type)
|
|
{
|
|
case SADB_EALG_3DESCBC:
|
|
iv_len = 8;
|
|
if (!entry->e_key || (entry->e_keylen == 0))
|
|
{
|
|
printf("hip_esp_encrypt: 3-DES key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
case SADB_X_EALG_BLOWFISHCBC:
|
|
iv_len = 8;
|
|
if (!entry->bf_key)
|
|
{
|
|
printf("hip_esp_encrypt: BLOWFISH key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
case SADB_EALG_NULL:
|
|
iv_len = 0;
|
|
break;
|
|
case SADB_X_EALG_AESCBC:
|
|
iv_len = 16;
|
|
if (!entry->aes_key && entry->e_key)
|
|
{
|
|
entry->aes_key = malloc(sizeof(AES_KEY));
|
|
if (AES_set_encrypt_key(entry->e_key, 8 *
|
|
entry->e_keylen,
|
|
entry->aes_key))
|
|
{
|
|
printf("hip_esp_encrypt: AES key problem!\n");
|
|
}
|
|
}
|
|
else if (!entry->aes_key)
|
|
{
|
|
printf("hip_esp_encrypt: AES key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
default:
|
|
printf("Unsupported encryption transform (%d).\n",
|
|
entry->e_type);
|
|
#ifdef HIP_VPLS
|
|
touchHeartbeat = 0;
|
|
#endif
|
|
return(-1);
|
|
break;
|
|
}
|
|
|
|
/* Add initialization vector (random value) */
|
|
if (iv_len > 0)
|
|
{
|
|
RAND_bytes(cbc_iv, iv_len);
|
|
memcpy(esp->enc_data, cbc_iv, iv_len);
|
|
padlen = iv_len - ((elen + 2) % iv_len);
|
|
}
|
|
else
|
|
{
|
|
/* Padding with NULL not based on IV length */
|
|
padlen = 4 - ((elen + 2) % 4);
|
|
}
|
|
/* add padding to input data, set padinfo */
|
|
location = hdr_len + elen;
|
|
for (i = 0; i < padlen; i++)
|
|
{
|
|
in[location + i] = i + 1;
|
|
}
|
|
padinfo = (struct ip_esp_padinfo*) &in[location + padlen];
|
|
padinfo->pad_length = padlen;
|
|
padinfo->next_hdr = (family == AF_INET) ? iph->ip_p : ip6h->ip6_nxt;
|
|
#ifdef HIP_VPLS
|
|
if (family == AF_UNSPEC)
|
|
{
|
|
padinfo->next_hdr = 0;
|
|
}
|
|
#endif
|
|
/* padinfo is encrypted too */
|
|
elen += padlen + 2;
|
|
|
|
/* Apply the encryption cipher directly into out buffer
|
|
* to avoid extra copying */
|
|
switch (entry->e_type)
|
|
{
|
|
case SADB_EALG_3DESCBC:
|
|
des_ede3_cbc_encrypt(&in[hdr_len],
|
|
&esp->enc_data[iv_len], elen,
|
|
entry->ks[0], entry->ks[1], entry->ks[2],
|
|
(des_cblock*)cbc_iv, DES_ENCRYPT);
|
|
break;
|
|
case SADB_X_EALG_BLOWFISHCBC:
|
|
BF_cbc_encrypt(&in[hdr_len],
|
|
&esp->enc_data[iv_len], elen,
|
|
entry->bf_key, cbc_iv, BF_ENCRYPT);
|
|
break;
|
|
case SADB_EALG_NULL:
|
|
memcpy(esp->enc_data, &in[hdr_len], elen);
|
|
break;
|
|
case SADB_X_EALG_AESCBC:
|
|
AES_cbc_encrypt(&in[hdr_len],
|
|
&esp->enc_data[iv_len], elen,
|
|
entry->aes_key, cbc_iv, AES_ENCRYPT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
elen += iv_len; /* auth will include IV */
|
|
*outlen += elen;
|
|
|
|
/*
|
|
* Authentication
|
|
*/
|
|
switch (entry->a_type)
|
|
{
|
|
case SADB_AALG_MD5HMAC:
|
|
alen = HMAC_SHA_96_BITS / 8; /* 12 bytes */
|
|
if (!entry->a_key || (entry->a_keylen == 0))
|
|
{
|
|
printf("auth err: missing keys\n");
|
|
return(-1);
|
|
}
|
|
elen += sizeof(struct ip_esp_hdr);
|
|
HMAC( EVP_md5(), entry->a_key, entry->a_keylen,
|
|
(__u8*)esp, elen, hmac_md, &hmac_md_len);
|
|
memcpy(&out[elen + (use_udp ? sizeof(udphdr) : 0)],
|
|
hmac_md, alen);
|
|
*outlen += alen;
|
|
break;
|
|
case SADB_AALG_SHA1HMAC:
|
|
alen = HMAC_SHA_96_BITS / 8; /* 12 bytes */
|
|
if (!entry->a_key || (entry->a_keylen == 0))
|
|
{
|
|
printf("auth err: missing keys\n");
|
|
return(-1);
|
|
}
|
|
elen += sizeof(struct ip_esp_hdr);
|
|
HMAC( EVP_sha1(), entry->a_key, entry->a_keylen,
|
|
(__u8*)esp, elen, hmac_md, &hmac_md_len);
|
|
memcpy(&out[elen + (use_udp ? sizeof(udphdr) : 0)],
|
|
hmac_md, alen);
|
|
*outlen += alen;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifndef HIP_VPLS
|
|
/* Record the address family of this packet, so incoming
|
|
* replies of the same protocol/ports can be matched to
|
|
* the same family.
|
|
*/
|
|
if (hip_add_proto_sel_entry(LSI4(&entry->lsi),
|
|
(__u8)(iph ? iph->ip_p : ip6h->ip6_nxt),
|
|
iph ? (__u8*)(iph + 1) : (__u8*)(ip6h + 1),
|
|
family, 0, now ) < 0)
|
|
{
|
|
printf("hip_esp_encrypt(): error adding sel entry.\n");
|
|
}
|
|
|
|
|
|
/* Restore the checksum in the input data, in case this is
|
|
* a broadcast packet that needs to be re-sent to some other
|
|
* destination.
|
|
*/
|
|
if (checksum_fix > 0)
|
|
{
|
|
#ifdef __MACOSX__
|
|
if (iph->ip_p == IPPROTO_UDP)
|
|
{
|
|
((struct udphdr*)(iph + 1))->uh_sum = checksum_fix;
|
|
}
|
|
else if (iph->ip_p == IPPROTO_TCP)
|
|
{
|
|
((struct tcphdr*)(iph + 1))->th_sum = checksum_fix;
|
|
}
|
|
#else
|
|
if (iph->ip_p == IPPROTO_UDP)
|
|
{
|
|
((struct udphdr*)(iph + 1))->check = checksum_fix;
|
|
}
|
|
else if (iph->ip_p == IPPROTO_TCP)
|
|
{
|
|
((struct tcphdr*)(iph + 1))->check = checksum_fix;
|
|
}
|
|
#endif
|
|
}
|
|
#endif /* HIP_VPLS */
|
|
|
|
/*
|
|
* Build a UDP header at the beginning of out buffer.
|
|
*/
|
|
if (use_udp)
|
|
{
|
|
memset(udph, 0, sizeof(udphdr));
|
|
/* grab port numbers from sockaddr structures */
|
|
if (entry->src_addrs->addr.ss_family == AF_INET)
|
|
{
|
|
udph->src_port = ((struct sockaddr_in*)
|
|
&entry->src_addrs->addr)->sin_port;
|
|
}
|
|
if (entry->dst_addrs->addr.ss_family == AF_INET)
|
|
{
|
|
udph->dst_port = ((struct sockaddr_in*)
|
|
&entry->dst_addrs->addr)->sin_port;
|
|
}
|
|
if (udph->src_port == 0)
|
|
{
|
|
printf("Warning: default to src HIP_UDP_PORT %d\n",
|
|
HIP_UDP_PORT);
|
|
udph->src_port = htons(HIP_UDP_PORT);
|
|
}
|
|
if (udph->dst_port == 0)
|
|
{
|
|
printf("Warning: default to dst HIP_UDP_PORT %d\n",
|
|
HIP_UDP_PORT);
|
|
udph->dst_port = htons(HIP_UDP_PORT);
|
|
}
|
|
udph->len = htons((__u16) * outlen);
|
|
/* TODO: support IPv6 ESP over UDP here */
|
|
udph->checksum = checksum_udp_packet (out,
|
|
SA(&entry->src_addrs->
|
|
addr),
|
|
SA(&entry->dst_addrs->
|
|
addr));
|
|
}
|
|
|
|
if (entry->spinat)
|
|
{
|
|
#ifdef VERBOSE_MR_DEBUG
|
|
printf("Rewriting outgoing ESP SPI from 0x%x to 0x%x.\n",
|
|
ntohl(esp->spi), entry->spinat);
|
|
#endif /* VERBOSE_MR_DEBUG */
|
|
esp->spi = htonl(entry->spinat);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* hip_esp_decrypt()
|
|
*
|
|
* in: in pointer to IP header of ESP packet to decrypt
|
|
* len packet length
|
|
* out pointer of where to build decrypted packet
|
|
* offset offset where decrypted packet is stored: &out[offset]
|
|
* outlen length of new packet
|
|
* entry the SADB entry
|
|
* iph IPv4 header or NULL for IPv6
|
|
* now pointer to current time (avoid extra gettimeofday call)
|
|
*
|
|
* out: New packet is built in out, outlen.
|
|
* Returns 0 on success, -1 otherwise.
|
|
*
|
|
* Perform authentication and decryption of ESP packets.
|
|
*/
|
|
int hip_esp_decrypt(__u8 *in, int len, __u8 *out, int *offset, int *outlen,
|
|
hip_sadb_entry *entry, struct ip *iph, struct timeval *now)
|
|
{
|
|
int alen = 0, elen = 0, iv_len = 0;
|
|
unsigned int hmac_md_len;
|
|
struct ip_esp_hdr *esp;
|
|
/*udphdr *udph;*/
|
|
|
|
struct ip_esp_padinfo *padinfo = 0;
|
|
__u8 cbc_iv[16];
|
|
__u8 hmac_md[EVP_MAX_MD_SIZE];
|
|
#ifndef HIP_VPLS
|
|
__u64 dst_mac;
|
|
__u16 sum;
|
|
int family_out;
|
|
struct tcphdr *tcp = NULL;
|
|
struct udphdr *udp = NULL;
|
|
#endif /* HIP_VPLS */
|
|
int use_udp = FALSE;
|
|
__u32 new_seqno_hi = 0, loss;
|
|
__u64 shift;
|
|
struct sockaddr_storage dst;
|
|
|
|
if (!in || !out || !entry)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
|
|
if (entry->mode == 3) /*(HIP_ESP_OVER_UDP) */
|
|
{
|
|
use_udp = TRUE;
|
|
/*udph = (udphdr*) &in[sizeof(struct ip)];*/
|
|
esp = (struct ip_esp_hdr*)&in[sizeof(struct ip) +
|
|
sizeof(udphdr)];
|
|
/* TODO: IPv6 UDP here */
|
|
}
|
|
else /* not UDP-encapsulated */
|
|
{
|
|
if (iph) /* IPv4 */
|
|
{
|
|
esp = (struct ip_esp_hdr*) &in[sizeof(struct ip)];
|
|
}
|
|
else /* IPv6 - header not included */
|
|
{
|
|
esp = (struct ip_esp_hdr*) &in[0];
|
|
}
|
|
}
|
|
/* if (ntohl(esp->spi) != entry->spi)
|
|
* return(-1); *//* this check might be excessive */
|
|
|
|
/* An IPv6 header is larger than an IPv4 header, so data
|
|
* is decrypted into a buffer at the larger offset, since
|
|
* we do not know the (inner) IP version before decryption. */
|
|
*offset = sizeof(struct eth_hdr) + sizeof(struct ip6_hdr); /* 54 */
|
|
#ifdef HIP_VPLS
|
|
*offset = sizeof(struct eth_hdr); /* Tunnel mode */
|
|
#endif
|
|
|
|
/*
|
|
* Preliminary anti-replay check.
|
|
*/
|
|
if (esp_anti_replay_check_initial(entry, ntohl(esp->seq_no),
|
|
&new_seqno_hi))
|
|
{
|
|
printf("duplicate sequence number detected: %x\n",
|
|
ntohl(esp->seq_no));
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* Authentication
|
|
*/
|
|
switch (entry->a_type)
|
|
{
|
|
case SADB_AALG_MD5HMAC:
|
|
alen = HMAC_SHA_96_BITS / 8; /* 12 bytes */
|
|
elen = len - sizeof(struct ip_esp_hdr) - alen;
|
|
if (iph)
|
|
{
|
|
elen -= sizeof(struct ip);
|
|
}
|
|
if (use_udp) /* HIP_ESP_OVER_UDP */
|
|
{
|
|
elen -= sizeof(udphdr);
|
|
}
|
|
if (!entry->a_key || (entry->a_keylen == 0))
|
|
{
|
|
printf("auth err: missing keys\n");
|
|
return(-1);
|
|
}
|
|
HMAC( EVP_md5(), entry->a_key, entry->a_keylen,
|
|
(__u8*)esp, elen + sizeof(struct ip_esp_hdr),
|
|
hmac_md, &hmac_md_len);
|
|
if (memcmp(&in[len - alen], hmac_md, alen) != 0)
|
|
{
|
|
printf("auth err: MD5 auth failure\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
case SADB_AALG_SHA1HMAC:
|
|
alen = HMAC_SHA_96_BITS / 8; /* 12 bytes */
|
|
elen = len - sizeof(struct ip_esp_hdr) - alen;
|
|
if (iph)
|
|
{
|
|
elen -= sizeof(struct ip);
|
|
}
|
|
if (use_udp) /* HIP_ESP_OVER_UDP */
|
|
{
|
|
elen -= sizeof(udphdr);
|
|
}
|
|
if (!entry->a_key || (entry->a_keylen == 0))
|
|
{
|
|
printf("auth err: missing keys\n");
|
|
return(-1);
|
|
}
|
|
HMAC( EVP_sha1(), entry->a_key, entry->a_keylen,
|
|
(__u8*)esp, elen + sizeof(struct ip_esp_hdr),
|
|
hmac_md, &hmac_md_len);
|
|
if (memcmp(&in[len - alen], hmac_md, alen) != 0)
|
|
{
|
|
printf("auth err: SHA1 auth failure SPI=0x%x\n",
|
|
entry->spi);
|
|
return(-1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* update anti-replay window now that integrity has been verified */
|
|
shift = esp_update_anti_replay(entry, ntohl(esp->seq_no), new_seqno_hi);
|
|
if (shift == 1)
|
|
{
|
|
/* normal case - in-order packet has next sequence number */
|
|
}
|
|
else if (shift == 0)
|
|
{
|
|
if (entry->lost > 0) /* seqno within window, discount loss */
|
|
{
|
|
entry->lost--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* window has shifted by loss number of packets */
|
|
loss = (__u32)shift - 1;
|
|
entry->lost += loss;
|
|
/* track loss based on IPv4 destination locator */
|
|
if (iph)
|
|
{
|
|
dst.ss_family = AF_INET;
|
|
memcpy(SA2IP(&dst), &iph->ip_dst, SAIPLEN(&dst));
|
|
loss = hip_sadb_inc_loss(entry, loss, SA(&dst));
|
|
if (loss > MULTIHOMING_LOSS_THRESHOLD)
|
|
{
|
|
esp_signal_loss(entry->spi, loss, SA(&dst));
|
|
hip_sadb_reset_loss(entry, SA(&dst));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decryption
|
|
*/
|
|
switch (entry->e_type)
|
|
{
|
|
case SADB_EALG_3DESCBC:
|
|
iv_len = 8;
|
|
if (!entry->e_key || (entry->e_keylen == 0))
|
|
{
|
|
printf("hip_esp_decrypt: 3-DES key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
case SADB_X_EALG_BLOWFISHCBC:
|
|
iv_len = 8;
|
|
if (!entry->bf_key)
|
|
{
|
|
printf("hip_esp_decrypt: BLOWFISH key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
case SADB_EALG_NULL:
|
|
iv_len = 0;
|
|
break;
|
|
case SADB_X_EALG_AESCBC:
|
|
iv_len = 16;
|
|
if (!entry->aes_key && entry->e_key)
|
|
{
|
|
entry->aes_key = malloc(sizeof(AES_KEY));
|
|
if (AES_set_decrypt_key(entry->e_key, 8 *
|
|
entry->e_keylen,
|
|
entry->aes_key))
|
|
{
|
|
printf("hip_esp_decrypt: AES key problem!\n");
|
|
}
|
|
}
|
|
else if (!entry->aes_key)
|
|
{
|
|
printf("hip_esp_decrypt: AES key missing.\n");
|
|
return(-1);
|
|
}
|
|
break;
|
|
default:
|
|
printf("Unsupported decryption algorithm (%d)\n",
|
|
entry->e_type);
|
|
break;
|
|
}
|
|
memcpy(cbc_iv, esp->enc_data, iv_len);
|
|
elen -= iv_len; /* don't include iv as part of ciphertext */
|
|
|
|
switch (entry->e_type)
|
|
{
|
|
case SADB_EALG_3DESCBC:
|
|
des_ede3_cbc_encrypt(&esp->enc_data[iv_len], &out[*offset],elen,
|
|
entry->ks[0], entry->ks[1], entry->ks[2],
|
|
(des_cblock*)cbc_iv, DES_DECRYPT);
|
|
break;
|
|
case SADB_X_EALG_BLOWFISHCBC:
|
|
BF_cbc_encrypt(&esp->enc_data[iv_len], &out[*offset], elen,
|
|
entry->bf_key, cbc_iv, BF_DECRYPT);
|
|
break;
|
|
case SADB_EALG_NULL:
|
|
memcpy(&out[*offset], esp->enc_data, elen);
|
|
/* padinfo = (struct ip_esp_padinfo*) &in[len - alen - 2]; */
|
|
break;
|
|
case SADB_X_EALG_AESCBC:
|
|
AES_cbc_encrypt(&esp->enc_data[iv_len], &out[*offset], elen,
|
|
entry->aes_key, cbc_iv, AES_DECRYPT);
|
|
break;
|
|
default:
|
|
return(-1);
|
|
}
|
|
|
|
/* remove padding */
|
|
padinfo = (struct ip_esp_padinfo*) &out[*offset + elen - 2];
|
|
elen -= 2 + padinfo->pad_length;
|
|
|
|
#ifndef HIP_VPLS
|
|
/* determine address family for new packet based on
|
|
* decrypted upper layer protocol header
|
|
*/
|
|
family_out = hip_select_family_by_proto(LSI4(
|
|
&entry->lsi),
|
|
padinfo->next_hdr,
|
|
&out[*offset], now);
|
|
|
|
/* rewrite upper-layer checksum
|
|
* checksum based on HITs --> based on LSIs */
|
|
if (family_out == AF_INET)
|
|
{
|
|
switch (padinfo->next_hdr)
|
|
{
|
|
#ifdef __MACOSX__
|
|
case IPPROTO_TCP:
|
|
tcp = (struct tcphdr*)&out[*offset];
|
|
sum = htons(tcp->th_sum);
|
|
sum =
|
|
csum_hip_revert( LSI4(&entry->lsi),
|
|
htonl(g_tap_lsi),
|
|
sum, htons(entry->hit_magic));
|
|
tcp->th_sum = htons(sum);
|
|
break;
|
|
case IPPROTO_UDP:
|
|
udp = (struct udphdr*)&out[*offset];
|
|
sum = htons(udp->uh_sum);
|
|
sum =
|
|
csum_hip_revert( LSI4(&entry->lsi),
|
|
htonl(g_tap_lsi),
|
|
sum, htons(entry->hit_magic));
|
|
udp->uh_sum = htons(sum);
|
|
break;
|
|
#else /* __MACOSX__ */
|
|
case IPPROTO_TCP:
|
|
tcp = (struct tcphdr*)&out[*offset];
|
|
sum = htons(tcp->check);
|
|
sum =
|
|
csum_hip_revert( LSI4(&entry->lsi),
|
|
htonl(g_tap_lsi),
|
|
sum, htons(entry->hit_magic));
|
|
tcp->check = htons(sum);
|
|
break;
|
|
case IPPROTO_UDP:
|
|
udp = (struct udphdr*)&out[*offset];
|
|
sum = htons(udp->check);
|
|
sum =
|
|
csum_hip_revert( LSI4(&entry->lsi),
|
|
htonl(g_tap_lsi),
|
|
sum, htons(entry->hit_magic));
|
|
udp->check = htons(sum);
|
|
#endif /* __MACOSX__ */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set offset to index the beginning of the packet */
|
|
if (family_out == AF_INET) /* offset = 20 */
|
|
{
|
|
*offset -= (sizeof(struct eth_hdr) + sizeof(struct ip));
|
|
}
|
|
else /* offset = 0 */
|
|
{
|
|
*offset -= (sizeof(struct eth_hdr) + sizeof(struct ip6_hdr));
|
|
}
|
|
|
|
/* Ethernet header */
|
|
dst_mac = get_eth_addr(family_out,
|
|
(family_out == AF_INET) ? SA2IP(&entry->lsi) :
|
|
SA2IP(&entry->dst_hit));
|
|
add_eth_header(&out[*offset], dst_mac, g_tap_mac,
|
|
(family_out == AF_INET) ? 0x0800 : 0x86dd);
|
|
|
|
/* IP header */
|
|
if (family_out == AF_INET)
|
|
{
|
|
add_ipv4_header(&out[*offset + sizeof(struct eth_hdr)],
|
|
LSI4(&entry->lsi), htonl(g_tap_lsi), iph,
|
|
(__u16)(sizeof(struct ip) + elen),
|
|
padinfo->next_hdr);
|
|
*outlen = sizeof(struct eth_hdr) + sizeof(struct ip) + elen;
|
|
}
|
|
else
|
|
{
|
|
add_ipv6_header(&out[*offset + sizeof(struct eth_hdr)],
|
|
SA(&entry->src_hit), SA(&entry->dst_hit),
|
|
NULL, iph, (__u16)elen, padinfo->next_hdr);
|
|
*outlen = sizeof(struct eth_hdr) + sizeof(struct ip6_hdr) +
|
|
elen;
|
|
}
|
|
#else /* HIP_VPLS */
|
|
*outlen = sizeof(struct eth_hdr) + elen;
|
|
#endif /* HIP_VPLS */
|
|
|
|
/* previously, this happened after write(), but there
|
|
* is some problem with using the entry ptr then */
|
|
hip_sadb_inc_bytes(entry, *outlen - sizeof(struct eth_hdr), now, 0);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* rewrite_checksum()
|
|
*
|
|
* Rewrite the upper-later TCP/UDP checksum so it is based on the HITs
|
|
* (which are summed and passed in as __u16 magic).
|
|
* Returns the old checksum value, so it can be restored.
|
|
*/
|
|
__u16 rewrite_checksum(__u8 *data, __u16 magic)
|
|
{
|
|
struct ip *iph = (struct ip *)data;
|
|
struct tcphdr *tcp;
|
|
struct udphdr *udp;
|
|
__u16 ret = 0;
|
|
|
|
|
|
/* rewrite upper-layer checksum, so it is based on HITs */
|
|
switch (iph->ip_p)
|
|
{
|
|
case IPPROTO_TCP:
|
|
tcp = (struct tcphdr*)(iph + 1);
|
|
#ifdef __MACOSX__
|
|
ret = tcp->th_sum;
|
|
tcp->th_sum = csum_tcpudp_hip_nofold(
|
|
iph->ip_src.s_addr, iph->ip_dst.s_addr,
|
|
tcp->th_sum, magic);
|
|
#else
|
|
ret = tcp->check;
|
|
tcp->check = csum_tcpudp_hip_nofold(
|
|
iph->ip_src.s_addr, iph->ip_dst.s_addr,
|
|
tcp->check, magic);
|
|
#endif
|
|
break;
|
|
case IPPROTO_UDP:
|
|
udp = (struct udphdr*)(iph + 1);
|
|
#ifdef __MACOSX__
|
|
ret = udp->uh_sum;
|
|
udp->uh_sum = csum_tcpudp_hip_nofold(
|
|
iph->ip_src.s_addr, iph->ip_dst.s_addr,
|
|
udp->uh_sum, magic);
|
|
#else
|
|
ret = udp->check;
|
|
udp->check = csum_tcpudp_hip_nofold(
|
|
iph->ip_src.s_addr, iph->ip_dst.s_addr,
|
|
udp->check, magic);
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/*
|
|
* add_eth_header()
|
|
*
|
|
* Build an Ethernet header.
|
|
*/
|
|
void add_eth_header(__u8 *data, __u64 src, __u64 dst, __u32 type)
|
|
{
|
|
struct eth_hdr *eth = (struct eth_hdr*)data;
|
|
|
|
memcpy(eth->dst, &dst, 6);
|
|
memcpy(eth->src, &src, 6);
|
|
eth->type = htons((__u16)type);
|
|
}
|
|
|
|
/*
|
|
* add_ipv4_header()
|
|
*
|
|
* Build an IPv4 header, copying some parameters from an old ip header (old),
|
|
* src and dst in host byte order. old may be NULL.
|
|
*/
|
|
void add_ipv4_header(__u8 *data, __u32 src, __u32 dst, struct ip *old,
|
|
__u16 len, __u8 proto)
|
|
{
|
|
struct ip *iph = (struct ip*)data;
|
|
|
|
memset(iph, 0, sizeof(struct ip));
|
|
iph->ip_v = 4;
|
|
iph->ip_hl = 5;
|
|
iph->ip_tos = old ? old->ip_tos : 0; /* preserve TOS field */
|
|
iph->ip_len = htons(len);
|
|
iph->ip_id = old ? old->ip_id : 0; /* copy identification */
|
|
iph->ip_off = old ? old->ip_off : 0; /* copy fragmentation offset */
|
|
iph->ip_ttl = old ? old->ip_ttl : 255; /* preserve TTL */
|
|
iph->ip_p = proto;
|
|
iph->ip_sum = 0;
|
|
iph->ip_src.s_addr = htonl(src); /* assume host byte order */
|
|
iph->ip_dst.s_addr = htonl(dst);
|
|
|
|
/* add the header checksum */
|
|
iph->ip_sum = ip_fast_csum((__u8*)iph, iph->ip_hl);
|
|
}
|
|
|
|
/*
|
|
* add_ipv6_pseudo_header()
|
|
*
|
|
* Build an IPv6 pseudo-header for upper-layer checksum calculation.
|
|
*/
|
|
void add_ipv6_pseudo_header(__u8 *data, struct sockaddr *src,
|
|
struct sockaddr *dst, __u32 len, __u8 proto)
|
|
{
|
|
int l;
|
|
struct _ph {
|
|
__u32 ph_len;
|
|
__u8 ph_zero[3];
|
|
__u8 ph_next_header;
|
|
} *ph;
|
|
memset(data, 0, 40);
|
|
|
|
/* 16 bytes source address, 16 bytes destination address */
|
|
l = sizeof(struct in6_addr);
|
|
memcpy(&data[0], SA2IP(src), l);
|
|
memcpy(&data[l], SA2IP(dst), l);
|
|
l += sizeof(struct in6_addr);
|
|
/* upper-layer packet length, zero, next header */
|
|
ph = (struct _ph*) &data[l];
|
|
ph->ph_len = htonl(len);
|
|
memset(ph->ph_zero, 0, 3);
|
|
ph->ph_next_header = proto;
|
|
}
|
|
|
|
/*
|
|
* add_ipv6_header()
|
|
*
|
|
* Build an IPv6 header, copying some parameters from an old header (old),
|
|
* src and dst in network byte order.
|
|
*/
|
|
void add_ipv6_header(__u8 *data,
|
|
struct sockaddr *src,
|
|
struct sockaddr *dst,
|
|
struct ip6_hdr *old,
|
|
struct ip *old4,
|
|
__u16 len,
|
|
__u8 proto)
|
|
{
|
|
struct ip6_hdr *ip6h = (struct ip6_hdr*)data;
|
|
__u32 tc;
|
|
|
|
memset(ip6h, 0, sizeof(struct ip6_hdr));
|
|
ip6h->ip6_flow = 0; /* zero the version (4), TC (8), flow-ID (20) */
|
|
ip6h->ip6_vfc = 0x60;
|
|
ip6h->ip6_plen = htons(len);
|
|
ip6h->ip6_nxt = proto;
|
|
ip6h->ip6_hlim = 255;
|
|
memcpy(&ip6h->ip6_src, SA2IP(src), sizeof(struct in6_addr));
|
|
memcpy(&ip6h->ip6_dst, SA2IP(dst), sizeof(struct in6_addr));
|
|
|
|
/* Try to preserve flow label and hop limit where possible. */
|
|
if (old)
|
|
{
|
|
ip6h->ip6_flow = old->ip6_flow;
|
|
ip6h->ip6_hlim = old->ip6_hlim;
|
|
}
|
|
else if (old4)
|
|
{
|
|
tc = old4->ip_tos << 24;
|
|
ip6h->ip6_flow |= tc; /* 8 bits traffic class */
|
|
ip6h->ip6_hlim = old4->ip_ttl; /* __u8 */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get_mac_addr()
|
|
* Give a random 6-bit Ethernet address given an IPv4/IPv6 address.
|
|
*/
|
|
__u64 get_eth_addr(int family, __u8 *addr)
|
|
{
|
|
__u32 index = 0, *p;
|
|
int i, len;
|
|
__u64 r = 0;
|
|
|
|
if (!addr)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
/* sum the 32-bit words in address */
|
|
p = (__u32*) addr;
|
|
len = (family == AF_INET) ? 4 : 16;
|
|
for (i = 0; i < len; i += 4)
|
|
{
|
|
index += *p++;
|
|
}
|
|
|
|
/* use sum as index into array of Ethernet addresses */
|
|
index %= MAX_ETH_ADDRS;
|
|
memcpy(&r, ð_addrs[index], 6);
|
|
((char *)&r)[0] &= 0xFE; /* clear the multicast bit */
|
|
|
|
return(r);
|
|
}
|
|
|
|
/* helper for sending ESP message to hipd over the espsp socketpair */
|
|
void esp_send_to_hipd(char *data, int len, char *errmsg)
|
|
{
|
|
/* int i;
|
|
* printf("sending this to hipd:\n");
|
|
* for (i = 0; i < len; i ++) {
|
|
* printf("%02x ", data[i] & 0xFF);
|
|
* } */
|
|
#ifdef __WIN32__
|
|
if (send(espsp[0], data, len, 0) < 0)
|
|
{
|
|
#else
|
|
if (write(espsp[0], data, len) != len)
|
|
{
|
|
#endif /* __WIN32__ */
|
|
printf("%s write error: %s\n", errmsg, strerror(errno));
|
|
}
|
|
}
|
|
|
|
/* send an ESP_ACQUIRE_LSI message, which results in a
|
|
* call to start_base_exchange() in hipd */
|
|
void esp_start_base_exchange(struct sockaddr *lsi)
|
|
{
|
|
struct sockaddr_storage dst;
|
|
const int len = sizeof(espmsg) + sizeof(struct sockaddr_storage);
|
|
char msgbuff[sizeof(espmsg) + sizeof(struct sockaddr_storage)] = {0};
|
|
espmsg *msg = (espmsg*) &msgbuff[0];
|
|
|
|
/* lsi is in host byte order, convert to network for ACQUIRE message */
|
|
memcpy(&dst, lsi, sizeof(struct sockaddr_storage));
|
|
if (dst.ss_family == AF_INET)
|
|
{
|
|
LSI4(&dst) = htonl(LSI4(lsi));
|
|
}
|
|
msg->message_type = ESP_ACQUIRE_LSI;
|
|
msg->message_data = htonl(sizeof(struct sockaddr_storage));
|
|
memcpy(&msgbuff[sizeof(espmsg)], &dst, sizeof(struct sockaddr_storage));
|
|
esp_send_to_hipd((char*) msg, len, "esp_start_base_exchange()");
|
|
}
|
|
|
|
/* send an ESP_EXPIRE_SPI message, which results in a
|
|
* call to start_expire() in hipd */
|
|
void esp_start_expire(__u32 spi)
|
|
{
|
|
espmsg msg;
|
|
msg.message_type = ESP_EXPIRE_SPI;
|
|
msg.message_data = htonl(spi);
|
|
esp_send_to_hipd((char*) &msg, sizeof(msg), "esp_start_expire()");
|
|
}
|
|
|
|
/* send an ESP_UDP_CTL message, which results in a
|
|
* call to receive_udp_hip_packet() in hipd */
|
|
void esp_receive_udp_hip_packet(char *buff, int len)
|
|
{
|
|
char msgbuff[sizeof(espmsg) + BUFF_LEN];
|
|
espmsg *msg = (espmsg*) &msgbuff[0];
|
|
msg->message_type = ESP_UDP_CTL;
|
|
msg->message_data = htonl((__u32)len);
|
|
memcpy(&msgbuff[sizeof(espmsg)], buff, len);
|
|
esp_send_to_hipd( msgbuff, len + sizeof(espmsg),
|
|
"esp_receive_udp_hip_packet()");
|
|
}
|
|
|
|
/* send an ESP_ADDR_LOSS message to signal that lost ESP packets were detected
|
|
*/
|
|
void esp_signal_loss(__u32 spi, __u32 loss, struct sockaddr *dst)
|
|
{
|
|
const int len = sizeof(espmsg) + 2 * sizeof(__u32) + \
|
|
sizeof(struct sockaddr_storage);
|
|
char msgbuff[sizeof(espmsg) + 2 * sizeof(__u32) + \
|
|
sizeof(struct sockaddr_storage)];
|
|
struct _loss_data {
|
|
__u32 spi;
|
|
__u32 loss;
|
|
struct sockaddr_storage dst;
|
|
} *ld;
|
|
espmsg *msg = (espmsg*) &msgbuff[0];
|
|
|
|
memset(msgbuff, 0, len);
|
|
msg->message_type = ESP_ADDR_LOSS;
|
|
msg->message_data = htonl(len - sizeof(espmsg));
|
|
|
|
ld = (struct _loss_data *) &msgbuff[sizeof(espmsg)];
|
|
ld->spi = htonl(spi);
|
|
ld->loss = htonl(loss);
|
|
ld->dst.ss_family = dst->sa_family;
|
|
memcpy(SA2IP(&ld->dst), SA2IP(dst), SAIPLEN(dst));
|
|
/* printf("%s spi=0x%x loss=%u\n", __FUNCTION__, spi, loss); */
|
|
esp_send_to_hipd((char*) msg, len, "esp_signal_loss()");
|
|
}
|
|
|
|
/*
|
|
* update the sequence number counters in the sadb entry and return the next
|
|
* sequence number
|
|
*/
|
|
__u32 get_next_seqno(hip_sadb_entry *entry)
|
|
{
|
|
__u32 r = ++entry->sequence;
|
|
/* overflow of lower 32 bits */
|
|
if (r == 0)
|
|
{
|
|
r = ++entry->sequence; /* don't use zero */
|
|
entry->sequence_hi++;
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
/*
|
|
* Perform preliminary anti-replay verification on an ESP packet's sequence
|
|
* number against the receive window of the SA; sadb entry is not modified.
|
|
* Determine the high-order bits of a 64-bit ESN based on anti-replay packet
|
|
* window and received lower bits.
|
|
*
|
|
* Returns 0 if the check passes, 1 if the check fails.
|
|
* Returns value high-order ESN bits in sequqnce_hi.
|
|
*/
|
|
int esp_anti_replay_check_initial(hip_sadb_entry *entry, __u32 seqno,
|
|
__u32 *sequence_hi)
|
|
{
|
|
/* T: top of window */
|
|
__u32 replay_win_maxl = (__u32)(entry->replay_win_max & 0xFFFFFFFF);
|
|
/* B: botttom of window */
|
|
__u64 replay_win_min = entry->replay_win_max - REPLAY_WIN_SIZE + 1;
|
|
__u32 replay_win_minl = (__u32)(replay_win_min & 0xFFFFFFFF);
|
|
__u64 shift, esn;
|
|
int do_replay_check;
|
|
|
|
*sequence_hi = entry->sequence_hi;
|
|
|
|
/* RFC 4303 Appendix A Case A: window within one subspace */
|
|
if (replay_win_maxl >= REPLAY_WIN_SIZE - 1)
|
|
{
|
|
do_replay_check = 1;
|
|
if (seqno >= replay_win_minl)
|
|
{
|
|
if (seqno > replay_win_maxl)
|
|
{
|
|
/* seq number to the right of the window */
|
|
do_replay_check = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* assume seq number wrap around to next subspace */
|
|
(*sequence_hi)++;
|
|
do_replay_check = 0; /* new subspace */
|
|
}
|
|
/* RFC 4303 Appendix A Case B: window spans two seq no subspaces
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
do_replay_check = 0;
|
|
if (seqno >= replay_win_minl)
|
|
{
|
|
/* seq number from previous subspace;
|
|
* don't wrap 64-bit ESN space */
|
|
if (*sequence_hi > 0)
|
|
{
|
|
(*sequence_hi)--;
|
|
do_replay_check = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* seq number in current sequence_hi subspace */
|
|
if (seqno <= replay_win_maxl)
|
|
{
|
|
do_replay_check = 1;
|
|
}
|
|
/* else seq number to the right of the window */
|
|
}
|
|
}
|
|
|
|
/* check if this sequence number has already been seen in the
|
|
* receive window bitmap
|
|
*/
|
|
if (do_replay_check)
|
|
{
|
|
esn = ((__u64)(*sequence_hi) << 32) | seqno;
|
|
shift = entry->replay_win_max - esn;
|
|
if ((entry->replay_win_map >> (__u32)shift) & 0x1)
|
|
{
|
|
return(1); /* drop packet - duplicate detected */
|
|
}
|
|
}
|
|
return(0); /* pass packet */
|
|
}
|
|
|
|
/*
|
|
* Update the ESP anti-replay window using sequence number derived from
|
|
* initial checks. Under normal conditions, returns 1 as the next received
|
|
* packet is the next sequence number; return value > 1 indicates window
|
|
* shifting, and 0 indicates received packet within window.
|
|
*/
|
|
__u64 esp_update_anti_replay(hip_sadb_entry *entry, __u32 seqno,
|
|
__u32 seqno_hi)
|
|
{
|
|
__u64 esn = ((__u64)seqno_hi << 32) | seqno;
|
|
__u64 shift;
|
|
if (esn > entry->replay_win_max)
|
|
{
|
|
/* shift window to the left, new max seq no received */
|
|
shift = esn - entry->replay_win_max;
|
|
entry->replay_win_map = entry->replay_win_map << shift;
|
|
entry->replay_win_max = esn;
|
|
entry->replay_win_map |= 0x1;
|
|
entry->sequence_hi = seqno_hi;
|
|
}
|
|
else
|
|
{
|
|
/* update bit corresponding to esn in window */
|
|
shift = entry->replay_win_max - esn;
|
|
entry->replay_win_map |= 0x1 << shift;
|
|
return(0);
|
|
}
|
|
return(shift);
|
|
}
|
|
|