kernel-netlink: Define netlink buffer as an union having a netlink header
[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 /* this hook is currently only supported on Linux (systems like FreeBSD don't
40 * actually provide an interface to change the sequence numbers of SAs) */
41 #ifdef __linux__
42
43 #include <linux/xfrm.h>
44 #include <unistd.h>
45 #include <errno.h>
46
47 #include <processing/jobs/callback_job.h>
48 #include <plugins/kernel_netlink/kernel_netlink_shared.h>
49
50 #define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x))))
51
52 typedef struct private_reset_seq_t private_reset_seq_t;
53
54 /**
55 * Private data of an reset_seq_t object.
56 */
57 struct private_reset_seq_t {
58
59 /**
60 * Implements the hook_t interface.
61 */
62 hook_t hook;
63
64 /**
65 * Delay for reset
66 */
67 int delay;
68
69 /**
70 * Sequence number to set for outgoing packages
71 */
72 int oseq;
73 };
74
75 typedef struct reset_cb_data_t reset_cb_data_t;
76
77 /**
78 * Data needed for the callback job
79 */
80 struct reset_cb_data_t {
81
82 /**
83 * The SA to modify
84 */
85 struct xfrm_usersa_id usersa;
86
87 /**
88 * Sequence number to set for outgoing packages
89 */
90 int oseq;
91 };
92
93 /**
94 * Callback job
95 */
96 static job_requeue_t reset_cb(struct reset_cb_data_t *data)
97 {
98 netlink_buf_t request;
99 struct nlmsghdr *hdr;
100 struct xfrm_aevent_id *id;
101 struct rtattr *rthdr;
102 struct xfrm_replay_state *rpstate;
103 struct sockaddr_nl addr;
104 int s, len;
105
106 DBG1(DBG_CFG, "setting sequence number of SPI 0x%x to %d",
107 htonl(data->usersa.spi), data->oseq);
108
109 memset(&request, 0, sizeof(request));
110
111 hdr = &request.hdr;
112 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
113 hdr->nlmsg_seq = 201;
114 hdr->nlmsg_pid = getpid();
115 hdr->nlmsg_type = XFRM_MSG_NEWAE;
116 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id));
117
118 id = (struct xfrm_aevent_id*)NLMSG_DATA(hdr);
119 id->sa_id = data->usersa;
120
121 rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id);
122 rthdr->rta_type = XFRMA_REPLAY_VAL;
123 rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state));
124 hdr->nlmsg_len += rthdr->rta_len;
125
126 /* xfrm_replay_state is the structure the kernel uses for
127 * replay detection, and the oseq element contains the
128 * sequence number for outgoing packets. Currently, this
129 * function sets the other elements seq (records the number of
130 * incoming packets) and bitmask to zero, but they could be
131 * adjusted in the same way as oseq if required. */
132 rpstate = (struct xfrm_replay_state*)RTA_DATA(rthdr);
133 rpstate->oseq = data->oseq;
134
135 s = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM);
136 if (s == -1)
137 {
138 DBG1(DBG_CFG, "opening XFRM socket failed: %s", strerror(errno));
139 return JOB_REQUEUE_NONE;
140 }
141 memset(&addr, 0, sizeof(addr));
142 addr.nl_family = AF_NETLINK;
143 len = sendto(s, hdr, hdr->nlmsg_len, 0,
144 (struct sockaddr*)&addr, sizeof(addr));
145 if (len != hdr->nlmsg_len)
146 {
147 DBG1(DBG_CFG, "sending XFRM aevent failed: %s", strerror(errno));
148 }
149 close(s);
150 return JOB_REQUEUE_NONE;
151 }
152
153 /**
154 * Schedule sequence number reset job
155 */
156 static void schedule_reset_job(private_reset_seq_t *this, host_t *dst,
157 u_int32_t spi)
158 {
159 struct reset_cb_data_t *data;
160 chunk_t chunk;
161
162 INIT(data,
163 .usersa = {
164 .spi = spi,
165 .family = dst->get_family(dst),
166 .proto = IPPROTO_ESP,
167 },
168 .oseq = this->oseq,
169 );
170
171 chunk = dst->get_address(dst);
172 memcpy(&data->usersa.daddr, chunk.ptr,
173 min(chunk.len, sizeof(xfrm_address_t)));
174
175 lib->scheduler->schedule_job(lib->scheduler,
176 (job_t*)callback_job_create(
177 (void*)reset_cb, data, (void*)free, NULL),
178 this->delay);
179 }
180
181 METHOD(listener_t, child_updown, bool,
182 private_reset_seq_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
183 bool up)
184 {
185 if (up)
186 {
187 schedule_reset_job(this, ike_sa->get_other_host(ike_sa),
188 child_sa->get_spi(child_sa, FALSE));
189 }
190 return TRUE;
191 }
192
193 METHOD(hook_t, destroy, void,
194 private_reset_seq_t *this)
195 {
196 free(this);
197 }
198
199 /**
200 * Create the IKE_AUTH fill hook
201 */
202 hook_t *reset_seq_hook_create(char *name)
203 {
204 private_reset_seq_t *this;
205
206 INIT(this,
207 .hook = {
208 .listener = {
209 .child_updown = _child_updown,
210 },
211 .destroy = _destroy,
212 },
213 .delay = conftest->test->get_int(conftest->test,
214 "hooks.%s.delay", 10, name),
215 .oseq = conftest->test->get_int(conftest->test,
216 "hooks.%s.oseq", 0, name),
217 );
218
219 return &this->hook;
220 }
221
222 #endif /* __linux__ */