conftest: Make outgoing sequence number set by reset_seq configurable
[strongswan.git] / src / conftest / hooks / reset_seq.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15 /*
16 * Copyright (C) 2012 achelos GmbH
17 *
18 * Permission is hereby granted, free of charge, to any person obtaining a copy
19 * of this software and associated documentation files (the "Software"), to deal
20 * in the Software without restriction, including without limitation the rights
21 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22 * copies of the Software, and to permit persons to whom the Software is
23 * furnished to do so, subject to the following conditions:
24 *
25 * The above copyright notice and this permission notice shall be included in
26 * all copies or substantial portions of the Software.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 * THE SOFTWARE.
35 */
36
37 #include "hook.h"
38
39 #include <linux/xfrm.h>
40 #include <unistd.h>
41 #include <errno.h>
42
43 #include <processing/jobs/callback_job.h>
44 #include <plugins/kernel_netlink/kernel_netlink_shared.h>
45
46 #define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x))))
47
48 typedef struct private_reset_seq_t private_reset_seq_t;
49
50 /**
51 * Private data of an reset_seq_t object.
52 */
53 struct private_reset_seq_t {
54
55 /**
56 * Implements the hook_t interface.
57 */
58 hook_t hook;
59
60 /**
61 * Delay for reset
62 */
63 int delay;
64
65 /**
66 * Sequence number to set for outgoing packages
67 */
68 int oseq;
69 };
70
71 typedef struct reset_cb_data_t reset_cb_data_t;
72
73 /**
74 * Data needed for the callback job
75 */
76 struct reset_cb_data_t {
77
78 /**
79 * The SA to modify
80 */
81 struct xfrm_usersa_id usersa;
82
83 /**
84 * Sequence number to set for outgoing packages
85 */
86 int oseq;
87 };
88
89 /**
90 * Callback job
91 */
92 static job_requeue_t reset_cb(struct reset_cb_data_t *data)
93 {
94 netlink_buf_t request;
95 struct nlmsghdr *hdr;
96 struct xfrm_aevent_id *id;
97 struct rtattr *rthdr;
98 struct xfrm_replay_state *rpstate;
99 struct sockaddr_nl addr;
100 int s, len;
101
102 DBG1(DBG_CFG, "setting sequence number of SPI 0x%x to %d",
103 htonl(data->usersa.spi), data->oseq);
104
105 memset(&request, 0, sizeof(request));
106
107 hdr = (struct nlmsghdr*)request;
108 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
109 hdr->nlmsg_seq = 201;
110 hdr->nlmsg_pid = getpid();
111 hdr->nlmsg_type = XFRM_MSG_NEWAE;
112 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id));
113
114 id = (struct xfrm_aevent_id*)NLMSG_DATA(hdr);
115 id->sa_id = data->usersa;
116
117 rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id);
118 rthdr->rta_type = XFRMA_REPLAY_VAL;
119 rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state));
120 hdr->nlmsg_len += rthdr->rta_len;
121
122 /* xfrm_replay_state is the structure the kernel uses for
123 * replay detection, and the oseq element contains the
124 * sequence number for outgoing packets. Currently, this
125 * function sets the other elements seq (records the number of
126 * incoming packets) and bitmask to zero, but they could be
127 * adjusted in the same way as oseq if required. */
128 rpstate = (struct xfrm_replay_state*)RTA_DATA(rthdr);
129 rpstate->oseq = data->oseq;
130
131 s = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM);
132 if (s == -1)
133 {
134 DBG1(DBG_CFG, "opening XFRM socket failed: %s", strerror(errno));
135 return JOB_REQUEUE_NONE;
136 }
137 memset(&addr, 0, sizeof(addr));
138 addr.nl_family = AF_NETLINK;
139 len = sendto(s, hdr, hdr->nlmsg_len, 0,
140 (struct sockaddr*)&addr, sizeof(addr));
141 if (len != hdr->nlmsg_len)
142 {
143 DBG1(DBG_CFG, "sending XFRM aevent failed: %s", strerror(errno));
144 }
145 close(s);
146 return JOB_REQUEUE_NONE;
147 }
148
149 /**
150 * Schedule sequence number reset job
151 */
152 static void schedule_reset_job(private_reset_seq_t *this, host_t *dst,
153 u_int32_t spi)
154 {
155 struct reset_cb_data_t *data;
156 chunk_t chunk;
157
158 INIT(data,
159 .usersa = {
160 .spi = spi,
161 .family = dst->get_family(dst),
162 .proto = IPPROTO_ESP,
163 },
164 .oseq = this->oseq,
165 );
166
167 chunk = dst->get_address(dst);
168 memcpy(&data->usersa.daddr, chunk.ptr,
169 min(chunk.len, sizeof(xfrm_address_t)));
170
171 lib->scheduler->schedule_job(lib->scheduler,
172 (job_t*)callback_job_create(
173 (void*)reset_cb, data, (void*)free, NULL),
174 this->delay);
175 }
176
177 METHOD(listener_t, child_updown, bool,
178 private_reset_seq_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
179 bool up)
180 {
181 if (up)
182 {
183 schedule_reset_job(this, ike_sa->get_other_host(ike_sa),
184 child_sa->get_spi(child_sa, FALSE));
185 }
186 return TRUE;
187 }
188
189 METHOD(hook_t, destroy, void,
190 private_reset_seq_t *this)
191 {
192 free(this);
193 }
194
195 /**
196 * Create the IKE_AUTH fill hook
197 */
198 hook_t *reset_seq_hook_create(char *name)
199 {
200 private_reset_seq_t *this;
201
202 INIT(this,
203 .hook = {
204 .listener = {
205 .child_updown = _child_updown,
206 },
207 .destroy = _destroy,
208 },
209 .delay = conftest->test->get_int(conftest->test,
210 "hooks.%s.delay", 10, name),
211 .oseq = conftest->test->get_int(conftest->test,
212 "hooks.%s.oseq", 0, name),
213 );
214
215 return &this->hook;
216 }