Index: lib/libipsec/pfkey.c =================================================================== --- lib/libipsec/pfkey.c +++ lib/libipsec/pfkey.c @@ -1825,6 +1825,7 @@ case SADB_X_EXT_SA_REPLAY: case SADB_X_EXT_NEW_ADDRESS_SRC: case SADB_X_EXT_NEW_ADDRESS_DST: + case SADB_X_EXT_SA_TFC_LENGTH: mhp[ext->sadb_ext_type] = (caddr_t)ext; break; default: Index: lib/libipsec/pfkey_dump.c =================================================================== --- lib/libipsec/pfkey_dump.c +++ lib/libipsec/pfkey_dump.c @@ -205,6 +205,7 @@ struct sadb_ident *m_sid, *m_did; struct sadb_sens *m_sens; struct sadb_x_sa_replay *m_sa_replay; + struct sadb_x_sa_tfc_length *m_sa_tfc_length; struct sadb_x_nat_t_type *natt_type; struct sadb_x_nat_t_port *natt_sport, *natt_dport; struct sadb_address *natt_oai, *natt_oar; @@ -233,6 +234,7 @@ m_did = (struct sadb_ident *)mhp[SADB_EXT_IDENTITY_DST]; m_sens = (struct sadb_sens *)mhp[SADB_EXT_SENSITIVITY]; m_sa_replay = (struct sadb_x_sa_replay *)mhp[SADB_X_EXT_SA_REPLAY]; + m_sa_tfc_length = (struct sadb_x_sa_tfc_length *)mhp[SADB_X_EXT_SA_TFC_LENGTH]; natt_type = (struct sadb_x_nat_t_type *)mhp[SADB_X_EXT_NAT_T_TYPE]; natt_sport = (struct sadb_x_nat_t_port *)mhp[SADB_X_EXT_NAT_T_SPORT]; natt_dport = (struct sadb_x_nat_t_port *)mhp[SADB_X_EXT_NAT_T_DPORT]; @@ -325,6 +327,10 @@ m_sa->sadb_sa_replay, m_sa->sadb_sa_flags); + /* tfc padding length */ + printf("tfcpadding=%d ", m_sa_tfc_length ? + m_sa_tfc_length->sadb_x_sa_tfc_length_length : 0); + /* state */ printf("state="); GETMSGSTR(str_state, m_sa->sadb_sa_state); Index: sys/net/pfkeyv2.h =================================================================== --- sys/net/pfkeyv2.h +++ sys/net/pfkeyv2.h @@ -297,6 +297,14 @@ }; _Static_assert(sizeof(struct sadb_x_sa_replay) == 8, "struct size mismatch"); +/* TFC padding length */ +struct sadb_x_sa_tfc_length { + u_int16_t sadb_x_sa_tfc_length_len; + u_int16_t sadb_x_sa_tfc_length_exttype; + int32_t sadb_x_sa_tfc_length_length; +}; +_Static_assert(sizeof(struct sadb_x_sa_tfc_length) == 8, "struct size mismatch"); + #define SADB_EXT_RESERVED 0 #define SADB_EXT_SA 1 #define SADB_EXT_LIFETIME_CURRENT 2 @@ -327,7 +335,8 @@ #define SADB_X_EXT_SA_REPLAY 26 /* Replay window override. */ #define SADB_X_EXT_NEW_ADDRESS_SRC 27 #define SADB_X_EXT_NEW_ADDRESS_DST 28 -#define SADB_EXT_MAX 28 +#define SADB_X_EXT_SA_TFC_LENGTH 29 +#define SADB_EXT_MAX 29 #define SADB_SATYPE_UNSPEC 0 #define SADB_SATYPE_AH 2 Index: sys/netipsec/ipsec_mbuf.c =================================================================== --- sys/netipsec/ipsec_mbuf.c +++ sys/netipsec/ipsec_mbuf.c @@ -215,7 +215,15 @@ if (pad > M_TRAILINGSPACE(m0)) { /* Add an mbuf to the chain. */ - MGET(m1, M_NOWAIT, MT_DATA); + if (pad > MCLBYTES) { + m_freem(m0); + DPRINTF(("%s: padding is too big (%d, max %d)\n", __func__, pad, MCLBYTES)); + return NULL; + } + if (pad > MHLEN) + m1 = m_getcl(M_NOWAIT, MT_DATA, 0); + else + MGET(m1, M_NOWAIT, MT_DATA); if (m1 == NULL) { m_freem(m0); DPRINTF(("%s: unable to get extra mbuf\n", __func__)); Index: sys/netipsec/key.c =================================================================== --- sys/netipsec/key.c +++ sys/netipsec/key.c @@ -385,6 +385,7 @@ sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */ sizeof(struct sadb_address), /* SADB_X_EXT_NEW_ADDRESS_SRC */ sizeof(struct sadb_address), /* SADB_X_EXT_NEW_ADDRESS_DST */ + sizeof(struct sadb_x_sa_tfc_length),/* SADB_X_EXT_SA_TFC_LENGTH */ }; _Static_assert(sizeof(minsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch"); @@ -418,6 +419,7 @@ sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */ 0, /* SADB_X_EXT_NEW_ADDRESS_SRC */ 0, /* SADB_X_EXT_NEW_ADDRESS_DST */ + sizeof(struct sadb_x_sa_tfc_length),/* SADB_X_EXT_SA_TFC_LENGTH */ }; _Static_assert(sizeof(maxsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch"); @@ -666,6 +668,7 @@ static struct mbuf *key_setsadbxtype(u_int16_t); static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t); static struct mbuf *key_setsadbxsareplay(u_int32_t); +static struct mbuf *key_setsadbxsatfclength(int32_t); static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t, u_int32_t, u_int32_t); static struct seckey *key_dup_keymsg(const struct sadb_key *, size_t, @@ -3377,6 +3380,45 @@ } } + /* TFC padding */ + sav->tfc = 0; + if (!SADB_CHECKHDR(mhp, SADB_X_EXT_SA_TFC_LENGTH)) { + int maxpacketsize; + if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA_TFC_LENGTH)) { + error = EINVAL; + goto fail; + } + error = 0; + sav->tfc = ((const struct sadb_x_sa_tfc_length*) + mhp->ext[SADB_X_EXT_SA_TFC_LENGTH])->sadb_x_sa_tfc_length_length; + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + maxpacketsize = IP_MAXPACKET; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + maxpacketsize = IPV6_MAXPACKET; + break; +#endif /* INET6 */ + default: + DPRINTF(("%s: unknown/unsupported protocol " + "family %d, SA %s/%08lx\n", __func__, + saidx->dst.sa.sa_family, ipsec_address(&saidx->dst, + buf, sizeof(buf)), (u_long) ntohl(sav->spi))); + error = EPFNOSUPPORT; + goto fail; + } + if (sav->tfc > maxpacketsize) { + ipseclog((LOG_DEBUG, "%s: invalid tfc value.\n", + __func__)); + error = EINVAL; + goto fail; + } + } + /* Authentication keys */ if (!SADB_CHECKHDR(mhp, SADB_EXT_KEY_AUTH)) { if (SADB_CHECKLEN(mhp, SADB_EXT_KEY_AUTH)) { @@ -3560,7 +3602,7 @@ SADB_X_EXT_NAT_T_TYPE, SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT, SADB_X_EXT_NAT_T_OAI, SADB_X_EXT_NAT_T_OAR, - SADB_X_EXT_NAT_T_FRAG, + SADB_X_EXT_NAT_T_FRAG, SADB_X_EXT_SA_TFC_LENGTH, }; uint32_t replay_count; @@ -3598,6 +3640,15 @@ goto fail; break; + case SADB_X_EXT_SA_TFC_LENGTH: + if (sav->tfc <= 0) + continue; + + m = key_setsadbxsatfclength(sav->tfc); + if (!m) + goto fail; + break; + case SADB_EXT_ADDRESS_SRC: m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, &sav->sah->saidx.src.sa, @@ -3921,6 +3972,32 @@ return m; } +/* + * Set data into sadb_x_sa_tfc_length + */ +static struct mbuf * +key_setsadbxsatfclength(int32_t tfc_length) +{ + struct mbuf *m; + struct sadb_x_sa_tfc_length *p; + size_t len; + + len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa_tfc_length)); + m = m_get2(len, M_NOWAIT, MT_DATA, 0); + if (m == NULL) + return (NULL); + m_align(m, len); + m->m_len = len; + p = mtod(m, struct sadb_x_sa_tfc_length *); + + bzero(p, len); + p->sadb_x_sa_tfc_length_len = PFKEY_UNIT64(len); + p->sadb_x_sa_tfc_length_exttype = SADB_X_EXT_SA_TFC_LENGTH; + p->sadb_x_sa_tfc_length_length = tfc_length; + + return m; +} + /* * Set a type in sadb_x_nat_t_type. */ @@ -5840,6 +5917,14 @@ } sav->natt->cksum = cksum; } + + if (sav->tfc > 0) { + if (sav->tfc > sizeof(struct ip) + sizeof(struct udphdr)) + sav->tfc -= sizeof(struct ip) + sizeof(struct udphdr); + else + sav->tfc = 0; + } + return (0); } @@ -8069,6 +8154,7 @@ case SADB_X_EXT_SA_REPLAY: case SADB_X_EXT_NEW_ADDRESS_SRC: case SADB_X_EXT_NEW_ADDRESS_DST: + case SADB_X_EXT_SA_TFC_LENGTH: /* duplicate check */ /* * XXX Are there duplication payloads of either Index: sys/netipsec/key_debug.c =================================================================== --- sys/netipsec/key_debug.c +++ sys/netipsec/key_debug.c @@ -78,6 +78,7 @@ static void kdebug_sadb_x_sa2(struct sadb_ext *); static void kdebug_sadb_x_sa_replay(struct sadb_ext *); static void kdebug_sadb_x_natt(struct sadb_ext *); +static void kdebug_sadb_x_sa_tfc_length(struct sadb_ext *); #ifndef _KERNEL #define panic(fmt, ...) { printf(fmt, ## __VA_ARGS__); exit(-1); } @@ -253,6 +254,9 @@ case SADB_X_EXT_NAT_T_DPORT: kdebug_sadb_x_natt(ext); break; + case SADB_X_EXT_SA_TFC_LENGTH: + kdebug_sadb_x_sa_tfc_length(ext); + break; default: printf("%s: invalid ext_type %u\n", __func__, ext->sadb_ext_type); @@ -519,6 +523,20 @@ } } +static void +kdebug_sadb_x_sa_tfc_length(struct sadb_ext *ext) +{ + struct sadb_x_sa_tfc_length *tfc_length; + + /* sanity check */ + if (ext == NULL) + panic("%s: NULL pointer was passed.\n", __func__); + + tfc_length = (struct sadb_x_sa_tfc_length *)ext; + printf("sadb_x_sa_tfc_length{ length=%d }\n", + tfc_length->sadb_x_sa_tfc_length_length); +} + void kdebug_sadb_x_policy(struct sadb_ext *ext) { Index: sys/netipsec/keydb.h =================================================================== --- sys/netipsec/keydb.h +++ sys/netipsec/keydb.h @@ -185,6 +185,8 @@ uint64_t cntr; /* counter for GCM and CTR */ volatile u_int refcnt; /* reference count */ + + int32_t tfc; /* TFC padding length (ESPv3) */ }; #define SECASVAR_LOCK(_sav) mtx_lock((_sav)->lock) Index: sys/netipsec/xform_esp.c =================================================================== --- sys/netipsec/xform_esp.c +++ sys/netipsec/xform_esp.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -637,9 +638,10 @@ uint8_t *ivp; uint64_t cntr; crypto_session_t cryptoid; - int hlen, rlen, padding, blks, alen, i, roff; - int error, maxpacketsize; + int hlen, rlen, padding, blks, alen, i, j, roff; + int error, maxpacketsize, tfclen; uint8_t prot; + int pktlen; IPSEC_ASSERT(sav != NULL, ("null SA")); esph = sav->tdb_authalgxform; @@ -657,9 +659,6 @@ */ blks = MAX(4, espx->blocksize); /* Cipher blocksize */ - /* XXX clamp padding length a la KAME??? */ - padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; - alen = xform_ah_authsize(esph); ESPSTAT_INC(esps_output); @@ -686,15 +685,32 @@ error = EPFNOSUPPORT; goto bad; } + + pktlen = skip + hlen + rlen + alen; + if (sav->tfc > 0 && sav->tfc > pktlen) { + tfclen = sav->tfc - pktlen; + /* XXX clamp padding length a la KAME??? */ + padding = ((blks - ((rlen + tfclen + 2) % blks)) % blks) + 2; + pktlen += padding + tfclen; + if (pktlen > maxpacketsize && tfclen > blks) + tfclen -= blks; + } + else { + tfclen = 0; + /* XXX clamp padding length a la KAME??? */ + padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; + pktlen += padding; + } + /* DPRINTF(("%s: skip %d hlen %d rlen %d padding %d alen %d blksd %d\n", __func__, skip, hlen, rlen, padding, alen, blks)); */ - if (skip + hlen + rlen + padding + alen > maxpacketsize) { + if (pktlen > maxpacketsize) { DPRINTF(("%s: packet in SA %s/%08lx got too big " "(len %u, max len %u)\n", __func__, ipsec_address(&saidx->dst, buf, sizeof(buf)), (u_long) ntohl(sav->spi), - skip + hlen + rlen + padding + alen, maxpacketsize)); + pktlen, maxpacketsize)); ESPSTAT_INC(esps_toobig); error = EMSGSIZE; goto bad; @@ -746,6 +762,35 @@ cntr = sav->cntr++; SECASVAR_UNLOCK(sav); + /* + * Add TFC padding + */ + for (i = 0; i < tfclen; i += MCLBYTES) { + int pad_len = MIN(tfclen - i, MCLBYTES); + pad = (u_char*) m_pad(m, pad_len); + if (pad == NULL) { + DPRINTF(("%s: m_pad failed for SA %s/%08lx\n", __func__, + ipsec_address(&saidx->dst, buf, sizeof(buf)), + (u_long) ntohl(sav->spi))); + m = NULL; /* NB: free'd by m_pad */ + error = ENOBUFS; + goto bad; + } + + switch (sav->flags & SADB_X_EXT_PMASK) { + case SADB_X_EXT_PRAND: + (void) read_random(pad, pad_len); + break; + case SADB_X_EXT_PZERO: + bzero(pad, pad_len); + break; + case SADB_X_EXT_PSEQ: + for (j = 0; j < pad_len; j++) + pad[i] = i + j + 1; + break; + } + } + /* * Add padding -- better to do it ourselves than use the crypto engine, * although if/when we support compression, we'd have to do that.