Skip to content

Commit 1b1e0bc

Browse files
lxindavem330
authored andcommitted
sctp: add refcnt support for sh_key
With refcnt support for sh_key, chunks auth sh_keys can be decided before enqueuing it. Changing the active key later will not affect the chunks already enqueued. Furthermore, this is necessary when adding the support for authinfo for sendmsg in next patch. Note that struct sctp_chunk can't be grown due to that performance drop issue on slow cpu, so it just reuses head_skb memory for shkey in sctp_chunk. Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent c469012 commit 1b1e0bc

File tree

9 files changed

+104
-58
lines changed

9 files changed

+104
-58
lines changed

include/net/sctp/auth.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ struct sctp_auth_bytes {
6262
/* Definition for a shared key, weather endpoint or association */
6363
struct sctp_shared_key {
6464
struct list_head key_list;
65-
__u16 key_id;
6665
struct sctp_auth_bytes *key;
66+
refcount_t refcnt;
67+
__u16 key_id;
6768
};
6869

6970
#define key_for_each(__key, __list_head) \
@@ -103,8 +104,10 @@ int sctp_auth_send_cid(enum sctp_cid chunk,
103104
int sctp_auth_recv_cid(enum sctp_cid chunk,
104105
const struct sctp_association *asoc);
105106
void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
106-
struct sk_buff *skb,
107-
struct sctp_auth_chunk *auth, gfp_t gfp);
107+
struct sk_buff *skb, struct sctp_auth_chunk *auth,
108+
struct sctp_shared_key *ep_key, gfp_t gfp);
109+
void sctp_auth_shkey_release(struct sctp_shared_key *sh_key);
110+
void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key);
108111

109112
/* API Helpers */
110113
int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id);

include/net/sctp/sm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
263263
struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
264264
__u32 new_cum_tsn, size_t nstreams,
265265
struct sctp_fwdtsn_skip *skiplist);
266-
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc);
266+
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
267+
__u16 key_id);
267268
struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc,
268269
__u16 stream_num, __be16 *stream_list,
269270
bool out, bool in);

include/net/sctp/structs.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,12 @@ struct sctp_chunk {
577577
/* This points to the sk_buff containing the actual data. */
578578
struct sk_buff *skb;
579579

580-
/* In case of GSO packets, this will store the head one */
581-
struct sk_buff *head_skb;
580+
union {
581+
/* In case of GSO packets, this will store the head one */
582+
struct sk_buff *head_skb;
583+
/* In case of auth enabled, this will point to the shkey */
584+
struct sctp_shared_key *shkey;
585+
};
582586

583587
/* These are the SCTP headers by reverse order in a packet.
584588
* Note that some of these may happen more than once. In that
@@ -1995,6 +1999,7 @@ struct sctp_association {
19951999
* The current generated assocaition shared key (secret)
19962000
*/
19972001
struct sctp_auth_bytes *asoc_shared_key;
2002+
struct sctp_shared_key *shkey;
19982003

19992004
/* SCTP AUTH: hmac id of the first peer requested algorithm
20002005
* that we support.

net/sctp/auth.c

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,32 @@ struct sctp_shared_key *sctp_auth_shkey_create(__u16 key_id, gfp_t gfp)
101101
return NULL;
102102

103103
INIT_LIST_HEAD(&new->key_list);
104+
refcount_set(&new->refcnt, 1);
104105
new->key_id = key_id;
105106

106107
return new;
107108
}
108109

109110
/* Free the shared key structure */
110-
static void sctp_auth_shkey_free(struct sctp_shared_key *sh_key)
111+
static void sctp_auth_shkey_destroy(struct sctp_shared_key *sh_key)
111112
{
112113
BUG_ON(!list_empty(&sh_key->key_list));
113114
sctp_auth_key_put(sh_key->key);
114115
sh_key->key = NULL;
115116
kfree(sh_key);
116117
}
117118

119+
void sctp_auth_shkey_release(struct sctp_shared_key *sh_key)
120+
{
121+
if (refcount_dec_and_test(&sh_key->refcnt))
122+
sctp_auth_shkey_destroy(sh_key);
123+
}
124+
125+
void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key)
126+
{
127+
refcount_inc(&sh_key->refcnt);
128+
}
129+
118130
/* Destroy the entire key list. This is done during the
119131
* associon and endpoint free process.
120132
*/
@@ -128,7 +140,7 @@ void sctp_auth_destroy_keys(struct list_head *keys)
128140

129141
key_for_each_safe(ep_key, tmp, keys) {
130142
list_del_init(&ep_key->key_list);
131-
sctp_auth_shkey_free(ep_key);
143+
sctp_auth_shkey_release(ep_key);
132144
}
133145
}
134146

@@ -409,13 +421,19 @@ int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp)
409421

410422
sctp_auth_key_put(asoc->asoc_shared_key);
411423
asoc->asoc_shared_key = secret;
424+
asoc->shkey = ep_key;
412425

413426
/* Update send queue in case any chunk already in there now
414427
* needs authenticating
415428
*/
416429
list_for_each_entry(chunk, &asoc->outqueue.out_chunk_list, list) {
417-
if (sctp_auth_send_cid(chunk->chunk_hdr->type, asoc))
430+
if (sctp_auth_send_cid(chunk->chunk_hdr->type, asoc)) {
418431
chunk->auth = 1;
432+
if (!chunk->shkey) {
433+
chunk->shkey = asoc->shkey;
434+
sctp_auth_shkey_hold(chunk->shkey);
435+
}
436+
}
419437
}
420438

421439
return 0;
@@ -703,16 +721,15 @@ int sctp_auth_recv_cid(enum sctp_cid chunk, const struct sctp_association *asoc)
703721
* after the AUTH chunk in the SCTP packet.
704722
*/
705723
void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
706-
struct sk_buff *skb,
707-
struct sctp_auth_chunk *auth,
708-
gfp_t gfp)
724+
struct sk_buff *skb, struct sctp_auth_chunk *auth,
725+
struct sctp_shared_key *ep_key, gfp_t gfp)
709726
{
710-
struct crypto_shash *tfm;
711727
struct sctp_auth_bytes *asoc_key;
728+
struct crypto_shash *tfm;
712729
__u16 key_id, hmac_id;
713-
__u8 *digest;
714730
unsigned char *end;
715731
int free_key = 0;
732+
__u8 *digest;
716733

717734
/* Extract the info we need:
718735
* - hmac id
@@ -724,12 +741,7 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
724741
if (key_id == asoc->active_key_id)
725742
asoc_key = asoc->asoc_shared_key;
726743
else {
727-
struct sctp_shared_key *ep_key;
728-
729-
ep_key = sctp_auth_get_shkey(asoc, key_id);
730-
if (!ep_key)
731-
return;
732-
744+
/* ep_key can't be NULL here */
733745
asoc_key = sctp_auth_asoc_create_secret(asoc, ep_key, gfp);
734746
if (!asoc_key)
735747
return;
@@ -829,7 +841,7 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
829841
struct sctp_association *asoc,
830842
struct sctp_authkey *auth_key)
831843
{
832-
struct sctp_shared_key *cur_key = NULL;
844+
struct sctp_shared_key *cur_key, *shkey;
833845
struct sctp_auth_bytes *key;
834846
struct list_head *sh_keys;
835847
int replace = 0;
@@ -842,46 +854,34 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
842854
else
843855
sh_keys = &ep->endpoint_shared_keys;
844856

845-
key_for_each(cur_key, sh_keys) {
846-
if (cur_key->key_id == auth_key->sca_keynumber) {
857+
key_for_each(shkey, sh_keys) {
858+
if (shkey->key_id == auth_key->sca_keynumber) {
847859
replace = 1;
848860
break;
849861
}
850862
}
851863

852-
/* If we are not replacing a key id, we need to allocate
853-
* a shared key.
854-
*/
855-
if (!replace) {
856-
cur_key = sctp_auth_shkey_create(auth_key->sca_keynumber,
857-
GFP_KERNEL);
858-
if (!cur_key)
859-
return -ENOMEM;
860-
}
864+
cur_key = sctp_auth_shkey_create(auth_key->sca_keynumber, GFP_KERNEL);
865+
if (!cur_key)
866+
return -ENOMEM;
861867

862868
/* Create a new key data based on the info passed in */
863869
key = sctp_auth_create_key(auth_key->sca_keylength, GFP_KERNEL);
864-
if (!key)
865-
goto nomem;
870+
if (!key) {
871+
kfree(cur_key);
872+
return -ENOMEM;
873+
}
866874

867875
memcpy(key->data, &auth_key->sca_key[0], auth_key->sca_keylength);
876+
cur_key->key = key;
868877

869-
/* If we are replacing, remove the old keys data from the
870-
* key id. If we are adding new key id, add it to the
871-
* list.
872-
*/
873-
if (replace)
874-
sctp_auth_key_put(cur_key->key);
875-
else
876-
list_add(&cur_key->key_list, sh_keys);
878+
if (replace) {
879+
list_del_init(&shkey->key_list);
880+
sctp_auth_shkey_release(shkey);
881+
}
882+
list_add(&cur_key->key_list, sh_keys);
877883

878-
cur_key->key = key;
879884
return 0;
880-
nomem:
881-
if (!replace)
882-
sctp_auth_shkey_free(cur_key);
883-
884-
return -ENOMEM;
885885
}
886886

887887
int sctp_auth_set_active_key(struct sctp_endpoint *ep,
@@ -952,7 +952,7 @@ int sctp_auth_del_key_id(struct sctp_endpoint *ep,
952952

953953
/* Delete the shared key */
954954
list_del_init(&key->key_list);
955-
sctp_auth_shkey_free(key);
955+
sctp_auth_shkey_release(key);
956956

957957
return 0;
958958
}

net/sctp/chunk.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
168168
{
169169
size_t len, first_len, max_data, remaining;
170170
size_t msg_len = iov_iter_count(from);
171+
struct sctp_shared_key *shkey = NULL;
171172
struct list_head *pos, *temp;
172173
struct sctp_chunk *chunk;
173174
struct sctp_datamsg *msg;
@@ -204,6 +205,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
204205
if (hmac_desc)
205206
max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +
206207
hmac_desc->hmac_len);
208+
209+
shkey = asoc->shkey;
207210
}
208211

209212
/* Check what's our max considering the above */
@@ -275,6 +278,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
275278
if (err < 0)
276279
goto errout_chunk_free;
277280

281+
chunk->shkey = shkey;
282+
278283
/* Put the chunk->skb back into the form expected by send. */
279284
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr -
280285
chunk->skb->data);

net/sctp/output.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,13 @@ static enum sctp_xmit sctp_packet_bundle_auth(struct sctp_packet *pkt,
241241
if (!chunk->auth)
242242
return retval;
243243

244-
auth = sctp_make_auth(asoc);
244+
auth = sctp_make_auth(asoc, chunk->shkey->key_id);
245245
if (!auth)
246246
return retval;
247247

248+
auth->shkey = chunk->shkey;
249+
sctp_auth_shkey_hold(auth->shkey);
250+
248251
retval = __sctp_packet_append_chunk(pkt, auth);
249252

250253
if (retval != SCTP_XMIT_OK)
@@ -490,7 +493,8 @@ static int sctp_packet_pack(struct sctp_packet *packet,
490493
}
491494

492495
if (auth) {
493-
sctp_auth_calculate_hmac(tp->asoc, nskb, auth, gfp);
496+
sctp_auth_calculate_hmac(tp->asoc, nskb, auth,
497+
packet->auth->shkey, gfp);
494498
/* free auth if no more chunks, or add it back */
495499
if (list_empty(&packet->chunk_list))
496500
sctp_chunk_free(packet->auth);
@@ -770,6 +774,16 @@ static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
770774
enum sctp_xmit retval = SCTP_XMIT_OK;
771775
size_t psize, pmtu, maxsize;
772776

777+
/* Don't bundle in this packet if this chunk's auth key doesn't
778+
* match other chunks already enqueued on this packet. Also,
779+
* don't bundle the chunk with auth key if other chunks in this
780+
* packet don't have auth key.
781+
*/
782+
if ((packet->auth && chunk->shkey != packet->auth->shkey) ||
783+
(!packet->auth && chunk->shkey &&
784+
chunk->chunk_hdr->type != SCTP_CID_AUTH))
785+
return SCTP_XMIT_PMTU_FULL;
786+
773787
psize = packet->size;
774788
if (packet->transport->asoc)
775789
pmtu = packet->transport->asoc->pathmtu;

net/sctp/sm_make_chunk.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ static void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len,
8787
/* Control chunk destructor */
8888
static void sctp_control_release_owner(struct sk_buff *skb)
8989
{
90-
/*TODO: do memory release */
90+
struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
91+
92+
if (chunk->shkey)
93+
sctp_auth_shkey_release(chunk->shkey);
9194
}
9295

9396
static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
@@ -102,7 +105,12 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
102105
*
103106
* For now don't do anything for now.
104107
*/
108+
if (chunk->auth) {
109+
chunk->shkey = asoc->shkey;
110+
sctp_auth_shkey_hold(chunk->shkey);
111+
}
105112
skb->sk = asoc ? asoc->base.sk : NULL;
113+
skb_shinfo(skb)->destructor_arg = chunk;
106114
skb->destructor = sctp_control_release_owner;
107115
}
108116

@@ -1271,7 +1279,8 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc,
12711279
return retval;
12721280
}
12731281

1274-
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
1282+
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
1283+
__u16 key_id)
12751284
{
12761285
struct sctp_authhdr auth_hdr;
12771286
struct sctp_hmac *hmac_desc;
@@ -1289,7 +1298,7 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
12891298
return NULL;
12901299

12911300
auth_hdr.hmac_id = htons(hmac_desc->hmac_id);
1292-
auth_hdr.shkey_id = htons(asoc->active_key_id);
1301+
auth_hdr.shkey_id = htons(key_id);
12931302

12941303
retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(auth_hdr),
12951304
&auth_hdr);

net/sctp/sm_statefuns.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,6 +4114,7 @@ static enum sctp_ierror sctp_sf_authenticate(
41144114
const union sctp_subtype type,
41154115
struct sctp_chunk *chunk)
41164116
{
4117+
struct sctp_shared_key *sh_key = NULL;
41174118
struct sctp_authhdr *auth_hdr;
41184119
__u8 *save_digest, *digest;
41194120
struct sctp_hmac *hmac;
@@ -4135,9 +4136,11 @@ static enum sctp_ierror sctp_sf_authenticate(
41354136
* configured
41364137
*/
41374138
key_id = ntohs(auth_hdr->shkey_id);
4138-
if (key_id != asoc->active_key_id && !sctp_auth_get_shkey(asoc, key_id))
4139-
return SCTP_IERROR_AUTH_BAD_KEYID;
4140-
4139+
if (key_id != asoc->active_key_id) {
4140+
sh_key = sctp_auth_get_shkey(asoc, key_id);
4141+
if (!sh_key)
4142+
return SCTP_IERROR_AUTH_BAD_KEYID;
4143+
}
41414144

41424145
/* Make sure that the length of the signature matches what
41434146
* we expect.
@@ -4166,7 +4169,7 @@ static enum sctp_ierror sctp_sf_authenticate(
41664169

41674170
sctp_auth_calculate_hmac(asoc, chunk->skb,
41684171
(struct sctp_auth_chunk *)chunk->chunk_hdr,
4169-
GFP_ATOMIC);
4172+
sh_key, GFP_ATOMIC);
41704173

41714174
/* Discard the packet if the digests do not match */
41724175
if (memcmp(save_digest, digest, sig_len)) {

net/sctp/socket.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
156156
/* The sndbuf space is tracked per association. */
157157
sctp_association_hold(asoc);
158158

159+
if (chunk->shkey)
160+
sctp_auth_shkey_hold(chunk->shkey);
161+
159162
skb_set_owner_w(chunk->skb, sk);
160163

161164
chunk->skb->destructor = sctp_wfree;
@@ -8109,6 +8112,9 @@ static void sctp_wfree(struct sk_buff *skb)
81098112
sk->sk_wmem_queued -= skb->truesize;
81108113
sk_mem_uncharge(sk, skb->truesize);
81118114

8115+
if (chunk->shkey)
8116+
sctp_auth_shkey_release(chunk->shkey);
8117+
81128118
sock_wfree(skb);
81138119
sctp_wake_up_waiters(sk, asoc);
81148120

0 commit comments

Comments
 (0)