📄 ipsec.c
字号:
case IPSEC_POLICY_ENTRUST:
default:
panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy);
}
siz = 0;
for (isr = sp->req; isr != NULL; isr = isr->next) {
clen = 0;
switch (isr->saidx.proto) {
case IPPROTO_ESP:
#ifdef IPSEC_ESP
clen = esp_hdrsiz(isr);
#else
clen = 0; /* XXX */
#endif
break;
case IPPROTO_AH:
clen = ah_hdrsiz(isr);
break;
case IPPROTO_IPCOMP:
clen = sizeof(struct ipcomp);
break;
}
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
switch (((struct sockaddr *)&isr->saidx.dst)->sa_family) {
case AF_INET:
clen += sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
clen += sizeof(struct ip6_hdr);
break;
#endif
default:
ipseclog((LOG_ERR, "ipsec_hdrsiz: "
"unknown AF %d in IPsec tunnel SA\n",
((struct sockaddr *)&isr->saidx.dst)->sa_family));
break;
}
}
siz += clen;
}
return siz;
}
/* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */
size_t
ipsec4_hdrsiz(m, dir, inp)
struct mbuf *m;
u_int dir;
struct inpcb *inp;
{
struct secpolicy *sp = NULL;
int error;
size_t size;
/* sanity check */
if (m == NULL)
return 0; /* XXX should be panic ? */
if (inp != NULL && inp->inp_socket == NULL)
panic("ipsec4_hdrsize: why is socket NULL but there is PCB.");
/* get SP for this packet.
* When we are called from ip_forward(), we call
* ipsec4_getpolicybyaddr() with IP_FORWARDING flag.
*/
if (inp == NULL)
sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
else
sp = ipsec4_getpolicybysock(m, dir, inp->inp_socket, &error);
if (sp == NULL)
return 0; /* XXX should be panic ? */
size = ipsec_hdrsiz(sp);
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP ipsec4_hdrsiz call free SP:%p\n", sp));
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("ipsec4_hdrsiz: size:%lu.\n", (unsigned long)size));
key_freesp(sp);
return size;
}
#ifdef INET6
/* This function is called from ipsec6_hdrsize_tcp(),
* and maybe from ip6_forward.()
*/
size_t
ipsec6_hdrsiz(m, dir, in6p)
struct mbuf *m;
u_int dir;
struct in6pcb *in6p;
{
struct secpolicy *sp = NULL;
int error;
size_t size;
/* sanity check */
if (m == NULL)
return 0; /* XXX shoud be panic ? */
if (in6p != NULL && in6p->in6p_socket == NULL)
panic("ipsec6_hdrsize: why is socket NULL but there is PCB.");
/* get SP for this packet */
/* XXX Is it right to call with IP_FORWARDING. */
if (in6p == NULL)
sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
else
sp = ipsec6_getpolicybysock(m, dir, in6p->in6p_socket, &error);
if (sp == NULL)
return 0;
size = ipsec_hdrsiz(sp);
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP ipsec6_hdrsiz call free SP:%p\n", sp));
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size));
key_freesp(sp);
return size;
}
#endif /* INET6 */
#ifdef INET
/*
* encapsulate for ipsec tunnel.
* ip->ip_src must be fixed later on.
*/
static int
ipsec4_encapsulate(m, sav)
struct mbuf *m;
struct secasvar *sav;
{
struct ip *oip;
struct ip *ip;
size_t hlen;
size_t plen;
/* can't tunnel between different AFs */
if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
|| ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
m_freem(m);
return EINVAL;
}
#if 0
/* XXX if the dst is myself, perform nothing. */
if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
m_freem(m);
return EINVAL;
}
#endif
if (m->m_len < sizeof(*ip))
panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
ip = mtod(m, struct ip *);
#ifdef _IP_VHL
hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
#else
hlen = ip->ip_hl << 2;
#endif
if (m->m_len != hlen)
panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
/* generate header checksum */
ip->ip_sum = 0;
#ifdef _IP_VHL
if (ip->ip_vhl == IP_VHL_BORING)
ip->ip_sum = in_cksum_hdr(ip);
else
ip->ip_sum = in_cksum(m, hlen);
#else
ip->ip_sum = in_cksum(m, hlen);
#endif
plen = m->m_pkthdr.len;
/*
* grow the mbuf to accomodate the new IPv4 header.
* NOTE: IPv4 options will never be copied.
*/
if (M_LEADINGSPACE(m->m_next) < hlen) {
struct mbuf *n;
MGET(n, M_DONTWAIT, MT_DATA);
if (!n) {
m_freem(m);
return ENOBUFS;
}
n->m_len = hlen;
n->m_next = m->m_next;
m->m_next = n;
m->m_pkthdr.len += hlen;
oip = mtod(n, struct ip *);
} else {
m->m_next->m_len += hlen;
m->m_next->m_data -= hlen;
m->m_pkthdr.len += hlen;
oip = mtod(m->m_next, struct ip *);
}
ip = mtod(m, struct ip *);
ovbcopy((caddr_t)ip, (caddr_t)oip, hlen);
m->m_len = sizeof(struct ip);
m->m_pkthdr.len -= (hlen - sizeof(struct ip));
/* construct new IPv4 header. see RFC 2401 5.1.2.1 */
/* ECN consideration. */
ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos);
#ifdef _IP_VHL
ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
#else
ip->ip_hl = sizeof(struct ip) >> 2;
#endif
ip->ip_off &= htons(~IP_OFFMASK);
ip->ip_off &= htons(~IP_MF);
switch (ip4_ipsec_dfbit) {
case 0: /* clear DF bit */
ip->ip_off &= htons(~IP_DF);
break;
case 1: /* set DF bit */
ip->ip_off |= htons(IP_DF);
break;
default: /* copy DF bit */
break;
}
ip->ip_p = IPPROTO_IPIP;
if (plen + sizeof(struct ip) < IP_MAXPACKET)
ip->ip_len = htons(plen + sizeof(struct ip));
else {
ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: "
"leave ip_len as is (invalid packet)\n"));
}
ip->ip_id = htons(ip_id++);
bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr,
&ip->ip_src, sizeof(ip->ip_src));
bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr,
&ip->ip_dst, sizeof(ip->ip_dst));
ip->ip_ttl = IPDEFTTL;
/* XXX Should ip_src be updated later ? */
return 0;
}
#endif /* INET */
#ifdef INET6
static int
ipsec6_encapsulate(m, sav)
struct mbuf *m;
struct secasvar *sav;
{
struct ip6_hdr *oip6;
struct ip6_hdr *ip6;
size_t plen;
/* can't tunnel between different AFs */
if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
|| ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET6) {
m_freem(m);
return EINVAL;
}
#if 0
/* XXX if the dst is myself, perform nothing. */
if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
m_freem(m);
return EINVAL;
}
#endif
plen = m->m_pkthdr.len;
/*
* grow the mbuf to accomodate the new IPv6 header.
*/
if (m->m_len != sizeof(struct ip6_hdr))
panic("ipsec6_encapsulate: assumption failed (first mbuf length)");
if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
struct mbuf *n;
MGET(n, M_DONTWAIT, MT_DATA);
if (!n) {
m_freem(m);
return ENOBUFS;
}
n->m_len = sizeof(struct ip6_hdr);
n->m_next = m->m_next;
m->m_next = n;
m->m_pkthdr.len += sizeof(struct ip6_hdr);
oip6 = mtod(n, struct ip6_hdr *);
} else {
m->m_next->m_len += sizeof(struct ip6_hdr);
m->m_next->m_data -= sizeof(struct ip6_hdr);
m->m_pkthdr.len += sizeof(struct ip6_hdr);
oip6 = mtod(m->m_next, struct ip6_hdr *);
}
ip6 = mtod(m, struct ip6_hdr *);
ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
/* Fake link-local scope-class addresses */
if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
oip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
oip6->ip6_dst.s6_addr16[1] = 0;
/* construct new IPv6 header. see RFC 2401 5.1.2.2 */
/* ECN consideration. */
ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
ip6->ip6_plen = htons(plen);
else {
/* ip6->ip6_plen will be updated in ip6_output() */
}
ip6->ip6_nxt = IPPROTO_IPV6;
bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr,
&ip6->ip6_src, sizeof(ip6->ip6_src));
bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr,
&ip6->ip6_dst, sizeof(ip6->ip6_dst));
ip6->ip6_hlim = IPV6_DEFHLIM;
/* XXX Should ip6_src be updated later ? */
return 0;
}
#endif /* INET6 */
/*
* Check the variable replay window.
* ipsec_chkreplay() performs replay check before ICV verification.
* ipsec_updatereplay() updates replay bitmap. This must be called after
* ICV verification (it also performs replay check, which is usually done
* beforehand).
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
*
* based on RFC 2401.
*/
int
ipsec_chkreplay(seq, sav)
u_int32_t seq;
struct secasvar *sav;
{
const struct secreplay *replay;
u_int32_t diff;
int fr;
u_int32_t wsizeb; /* constant: bits of window size */
int frlast; /* constant: last frame */
/* sanity check */
if (sav == NULL)
panic("ipsec_chkreplay: NULL pointer was passed.\n");
replay = sav->replay;
if (replay->wsize == 0)
return 1; /* no need to check replay. */
/* constant */
frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* sequence number of 0 is invalid */
if (seq == 0)
return 0;
/* first time is always okay */
if (replay->count == 0)
return 1;
if (seq > replay->lastseq) {
/* larger sequences are okay */
return 1;
} else {
/* seq is equal or less than lastseq. */
diff = replay->lastseq - seq;
/* over range to check, i.e. too old or wrapped */
if (diff >= wsizeb)
return 0;
fr = frlast - diff / 8;
/* this packet already seen ? */
if ((replay->bitmap)[fr] & (1 << (diff % 8)))
return 0;
/* out of order but good */
return 1;
}
}
/*
* check replay counter whether to update or not.
* OUT: 0: OK
* 1: NG
*/
int
ipsec_updatereplay(seq, sav)
u_int32_t seq;
struct secasvar *sav;
{
struct secreplay *replay;
u_int32_t diff;
int fr;
u_int32_t wsizeb; /* constant: bits of window size */
int frlast; /* constant: last frame */
/* sanity check */
if (sav == NULL)
panic("ipsec_chkreplay: NULL pointer was passed.\n");
replay = sav->replay;
if (replay->wsize == 0)
goto ok; /* no need to check replay. */
/* constant */
frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* sequence number of 0 is invalid */
if (seq == 0)
return 1;
/* first time */
if (replay->count == 0) {
replay->lastseq = seq;
bzero(replay->bitmap, replay->wsize);
(replay->bitmap)[frlast] = 1;
goto ok;
}
if (seq > replay->lastseq) {
/* seq is larger than lastseq. */
diff = seq - replay->lastseq;
/* new larger sequence number */
if (diff < wsizeb) {
/* In window */
/* set bit for this packet */
vshiftl(replay->bitmap, diff, replay->wsize);
(replay->bitmap)[frlast] |= 1;
} else {
/* this packet has a "way larger" */
bzero(replay->bitmap, replay->wsize);
(replay->bitmap)[frlast] = 1;
}
replay->lastseq = seq;
/* larger is good */
} else {
/* seq is equal or less than lastseq. */
diff = replay->lastseq - seq;
/* over range to check, i.e. too old or wrapped */
if (diff >= wsizeb)
return 1;
fr = frlast - diff / 8;
/* this packet already seen ? */
if ((replay->bitmap)[fr] & (1 << (diff % 8)))
return 1;
/* mark as seen */
(replay->bitmap)[fr] |= (1 << (diff % 8));
/* out of order but good */
}
ok:
if (replay->count == ~0) {
/* set overflow flag */
replay->overflow++;
/* don't increment, no more packets accepted */
if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
return 1;
ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n",
replay->overflow, ipsec_logsastr(sav)));
}
replay->count++;
return 0;
}
/*
* shift variable length buffer to left.
* IN: bitmap: pointer to the buffer
* nbit: the number of to shift.
* wsize: buffer size (bytes).
*/
static void
vshiftl(bitmap, nbit, wsize)
unsigned char *bitmap;
int nbit, wsize;
{
int s, j, i;
unsigned char over;
for (j = 0; j < n
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -