When adding support for sending SCTP packets containing an ABORT chunk

to ipfw in https://svnweb.freebsd.org/changeset/base/326233,
a dependency on the SCTP stack was added to ipfw by accident.

This was noted by Kevel Bowling in https://reviews.freebsd.org/D13594
where also a solution was suggested. This patch is based on Kevin's
suggestion, but implements the required SCTP checksum computation
without any dependency on other SCTP sources.

While there, do some cleanups and improve comments.

Thanks to Kevin Kevin Browling for reporting the issue and suggesting
a fix.
This commit is contained in:
Michael Tuexen 2017-12-26 12:35:02 +00:00
parent 81b5c8ff8d
commit cd90150413
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327200
3 changed files with 55 additions and 47 deletions

View File

@ -4255,7 +4255,7 @@ netinet/sctp_asconf.c optional inet sctp | inet6 sctp
netinet/sctp_auth.c optional inet sctp | inet6 sctp
netinet/sctp_bsd_addr.c optional inet sctp | inet6 sctp
netinet/sctp_cc_functions.c optional inet sctp | inet6 sctp
netinet/sctp_crc32.c optional inet sctp | inet6 sctp
netinet/sctp_crc32.c optional inet | inet6
netinet/sctp_indata.c optional inet sctp | inet6 sctp
netinet/sctp_input.c optional inet sctp | inet6 sctp
netinet/sctp_output.c optional inet sctp | inet6 sctp

View File

@ -35,29 +35,36 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_sctp.h"
#ifdef SCTP
#include <netinet/sctp_os.h>
#include <netinet/sctp.h>
#include <netinet/sctp_crc32.h>
#include <netinet/sctp_pcb.h>
#else
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <netinet/sctp_crc32.h>
#endif
static uint32_t
sctp_finalize_crc32c(uint32_t crc32c)
{
uint32_t result;
#if BYTE_ORDER == BIG_ENDIAN
uint8_t byte0, byte1, byte2, byte3;
#endif
/* Complement the result */
result = ~crc32c;
#if BYTE_ORDER == BIG_ENDIAN
/*
* For BIG-ENDIAN.. aka Motorola byte order the result is in
* little-endian form. So we must manually swap the bytes. Then we
* can call htonl() which does nothing...
* For BIG-ENDIAN platforms the result is in little-endian form. So
* we must swap the bytes to return the result in network byte
* order.
*/
byte0 = result & 0x000000ff;
byte1 = (result >> 8) & 0x000000ff;
@ -66,56 +73,54 @@ sctp_finalize_crc32c(uint32_t crc32c)
crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
#else
/*
* For INTEL platforms the result comes out in network order. No
* htonl is required or the swap above. So we optimize out both the
* htonl and the manual swap above.
* For LITTLE ENDIAN platforms the result is in already in network
* byte order.
*/
crc32c = result;
#endif
return (crc32c);
}
/*
* Compute the SCTP checksum in network byte order for a given mbuf chain m
* which contains an SCTP packet starting at offset.
* Since this function is also called by ipfw, don't assume that
* it is compiled on a kernel with SCTP support.
*/
uint32_t
sctp_calculate_cksum(struct mbuf *m, uint32_t offset)
{
/*
* given a mbuf chain with a packetheader offset by 'offset'
* pointing at a sctphdr (with csum set to 0) go through the chain
* of SCTP_BUF_NEXT()'s and calculate the SCTP checksum. This also
* has a side bonus as it will calculate the total length of the
* mbuf chain. Note: if offset is greater than the total mbuf
* length, checksum=1, pktlen=0 is returned (ie. no real error code)
*/
uint32_t base = 0xffffffff;
struct mbuf *at;
at = m;
/* find the correct mbuf and offset into mbuf */
while ((at != NULL) && (offset > (uint32_t)SCTP_BUF_LEN(at))) {
offset -= SCTP_BUF_LEN(at); /* update remaining offset
* left */
at = SCTP_BUF_NEXT(at);
while (offset > 0) {
KASSERT(m != NULL, ("sctp_calculate_cksum, offset > length of mbuf chain"));
if (offset < (uint32_t)m->m_len) {
break;
}
offset -= m->m_len;
m = m->m_next;
}
while (at != NULL) {
if ((SCTP_BUF_LEN(at) - offset) > 0) {
base = calculate_crc32c(base,
(unsigned char *)(SCTP_BUF_AT(at, offset)),
(unsigned int)(SCTP_BUF_LEN(at) - offset));
}
if (offset) {
/* we only offset once into the first mbuf */
if (offset < (uint32_t)SCTP_BUF_LEN(at))
offset = 0;
else
offset -= SCTP_BUF_LEN(at);
}
at = SCTP_BUF_NEXT(at);
if (offset > 0) {
base = calculate_crc32c(base,
(unsigned char *)(m->m_data + offset),
(unsigned int)(m->m_len - offset));
m = m->m_next;
}
while (m != NULL) {
base = calculate_crc32c(base,
(unsigned char *)m->m_data,
(unsigned int)m->m_len);
m = m->m_next;
}
base = sctp_finalize_crc32c(base);
return (base);
}
#ifdef SCTP
/*
* Compute and insert the SCTP checksum in network byte order for a given
* mbuf chain m which contains an SCTP packet starting at offset.
*/
void
sctp_delayed_cksum(struct mbuf *m, uint32_t offset)
{
@ -127,14 +132,15 @@ sctp_delayed_cksum(struct mbuf *m, uint32_t offset)
offset += offsetof(struct sctphdr, checksum);
if (offset + sizeof(uint32_t) > (uint32_t)(m->m_len)) {
SCTP_PRINTF("sctp_delayed_cksum(): m->len: %d, off: %d.\n",
(uint32_t)m->m_len, offset);
/*
* XXX this shouldn't happen, but if it does, the correct
* behavior may be to insert the checksum in the appropriate
* next mbuf in the chain.
*/
#ifdef INVARIANTS
panic("sctp_delayed_cksum(): m->m_len: %d, offset: %u.",
m->m_len, offset);
#else
SCTP_PRINTF("sctp_delayed_cksum(): m->m_len: %d, offset: %u.\n",
m->m_len, offset);
#endif
return;
}
*(uint32_t *)(m->m_data + offset) = checksum;
}
#endif

View File

@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$");
#if defined(_KERNEL)
uint32_t sctp_calculate_cksum(struct mbuf *, uint32_t);
#ifdef SCTP
void sctp_delayed_cksum(struct mbuf *, uint32_t offset);
#endif
#endif /* _KERNEL */
#endif /* __crc32c_h__ */