Fix fragmented UDP packets handling since rev.360967.

Consider IP_MF flag when checking length of the UDP packet to
match the declared value.

Sponsored by:	Sippy Software, Inc.
Differential Revision:	https://reviews.freebsd.org/D32363
MFC after:	2 weeks
This commit is contained in:
Maxim Sobolev 2021-10-07 13:41:40 -07:00
parent fc39305439
commit 461e6f23db

View File

@ -721,21 +721,37 @@ ProtoAliasOut(struct libalias *la, struct ip *pip,
return (PKT_ALIAS_IGNORED);
}
#define MF_ISSET(_pip) (ntohs((_pip)->ip_off) & IP_MF)
#define FRAG_NO_HDR(_pip) (ntohs((_pip)->ip_off) & IP_OFFMASK)
static struct udphdr *
ValidateUdpLength(struct ip *pip)
{
struct udphdr *ud;
size_t dlen;
#ifdef _KERNEL
KASSERT(!FRAG_NO_HDR(pip), ("header-less fragment isn't expected here"));
#endif
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct udphdr))
return (NULL);
ud = (struct udphdr *)ip_next(pip);
if (!MF_ISSET(pip) && dlen < ntohs(ud->uh_ulen))
return (NULL);
return (ud);
}
static int
UdpAliasIn(struct libalias *la, struct ip *pip)
{
struct udphdr *ud;
struct alias_link *lnk;
size_t dlen;
LIBALIAS_LOCK_ASSERT(la);
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct udphdr))
return (PKT_ALIAS_IGNORED);
ud = (struct udphdr *)ip_next(pip);
if (dlen < ntohs(ud->uh_ulen))
ud = ValidateUdpLength(pip);
if (ud == NULL)
return (PKT_ALIAS_IGNORED);
lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst,
@ -828,19 +844,14 @@ UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create)
u_short proxy_server_port;
int proxy_type;
int error;
size_t dlen;
LIBALIAS_LOCK_ASSERT(la);
ud = ValidateUdpLength(pip);
if (ud == NULL)
return (PKT_ALIAS_IGNORED);
/* Return if proxy-only mode is enabled and not proxyrule found.*/
dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
if (dlen < sizeof(struct udphdr))
return (PKT_ALIAS_IGNORED);
ud = (struct udphdr *)ip_next(pip);
if (dlen < ntohs(ud->uh_ulen))
return (PKT_ALIAS_IGNORED);
proxy_type = ProxyCheck(la, &proxy_server_address, &proxy_server_port,
pip->ip_src, pip->ip_dst, ud->uh_dport, pip->ip_p);
if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY))
@ -1339,64 +1350,65 @@ LibAliasInLocked(struct libalias *la, struct ip *pip, int maxpacketsize)
goto getout;
}
iresult = PKT_ALIAS_IGNORED;
if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasIn(la, pip);
break;
case IPPROTO_UDP:
iresult = UdpAliasIn(la, pip);
break;
case IPPROTO_TCP:
iresult = TcpAliasIn(la, pip);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_LOCAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
/* Walk out chain. */
error = find_handler(IN, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasIn(la, pip->ip_src,
pip, pip->ip_p, &pip->ip_sum);
break;
}
default:
iresult = ProtoAliasIn(la, pip->ip_src, pip,
pip->ip_p, &pip->ip_sum);
break;
}
if (ntohs(pip->ip_off) & IP_MF) {
struct alias_link *lnk;
lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id);
if (lnk != NULL) {
iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT;
SetFragmentAddr(lnk, pip->ip_dst);
} else {
iresult = PKT_ALIAS_ERROR;
}
}
} else {
if (FRAG_NO_HDR(pip)) {
iresult = FragmentIn(la, pip->ip_src, pip, pip->ip_id,
&pip->ip_sum);
goto getout;
}
iresult = PKT_ALIAS_IGNORED;
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasIn(la, pip);
break;
case IPPROTO_UDP:
iresult = UdpAliasIn(la, pip);
break;
case IPPROTO_TCP:
iresult = TcpAliasIn(la, pip);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_LOCAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
/* Walk out chain. */
error = find_handler(IN, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasIn(la, pip->ip_src,
pip, pip->ip_p, &pip->ip_sum);
break;
}
default:
iresult = ProtoAliasIn(la, pip->ip_src, pip,
pip->ip_p, &pip->ip_sum);
break;
}
if (MF_ISSET(pip)) {
struct alias_link *lnk;
lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id);
if (lnk != NULL) {
iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT;
SetFragmentAddr(lnk, pip->ip_dst);
} else {
iresult = PKT_ALIAS_ERROR;
}
}
getout:
@ -1492,52 +1504,55 @@ LibAliasOutLocked(struct libalias *la,
} else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) {
SetDefaultAliasAddress(la, pip->ip_src);
}
iresult = PKT_ALIAS_IGNORED;
if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasOut(la, pip, create);
break;
case IPPROTO_UDP:
iresult = UdpAliasOut(la, pip, maxpacketsize, create);
break;
case IPPROTO_TCP:
iresult = TcpAliasOut(la, pip, maxpacketsize, create);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_GLOBAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
/* Walk out chain. */
error = find_handler(OUT, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
default:
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
} else {
if (FRAG_NO_HDR(pip)) {
iresult = FragmentOut(la, pip, &pip->ip_sum);
goto getout_restore;
}
iresult = PKT_ALIAS_IGNORED;
switch (pip->ip_p) {
case IPPROTO_ICMP:
iresult = IcmpAliasOut(la, pip, create);
break;
case IPPROTO_UDP:
iresult = UdpAliasOut(la, pip, maxpacketsize, create);
break;
case IPPROTO_TCP:
iresult = TcpAliasOut(la, pip, maxpacketsize, create);
break;
#ifdef _KERNEL
case IPPROTO_SCTP:
iresult = SctpAlias(la, pip, SN_TO_GLOBAL);
break;
#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
.oaddr = NULL,
.aaddr = NULL,
.aport = NULL,
.sport = NULL,
.dport = NULL,
.maxpktsize = 0
};
/* Walk out chain. */
error = find_handler(OUT, IP, la, pip, &ad);
if (error == 0)
iresult = PKT_ALIAS_OK;
else
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
default:
iresult = ProtoAliasOut(la, pip,
pip->ip_dst, pip->ip_p, &pip->ip_sum, create);
break;
}
getout_restore:
SetDefaultAliasAddress(la, addr_save);
getout:
return (iresult);