diff options
Diffstat (limited to 'net/batman-adv/icmp_socket.c')
-rw-r--r-- | net/batman-adv/icmp_socket.c | 128 |
1 files changed, 80 insertions, 48 deletions
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 82ac6472fa6f..29ae4efe3543 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c | |||
@@ -29,7 +29,7 @@ | |||
29 | static struct batadv_socket_client *batadv_socket_client_hash[256]; | 29 | static struct batadv_socket_client *batadv_socket_client_hash[256]; |
30 | 30 | ||
31 | static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, | 31 | static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, |
32 | struct batadv_icmp_packet_rr *icmp_packet, | 32 | struct batadv_icmp_header *icmph, |
33 | size_t icmp_len); | 33 | size_t icmp_len); |
34 | 34 | ||
35 | void batadv_socket_init(void) | 35 | void batadv_socket_init(void) |
@@ -155,13 +155,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, | |||
155 | struct batadv_priv *bat_priv = socket_client->bat_priv; | 155 | struct batadv_priv *bat_priv = socket_client->bat_priv; |
156 | struct batadv_hard_iface *primary_if = NULL; | 156 | struct batadv_hard_iface *primary_if = NULL; |
157 | struct sk_buff *skb; | 157 | struct sk_buff *skb; |
158 | struct batadv_icmp_packet_rr *icmp_packet; | 158 | struct batadv_icmp_packet_rr *icmp_packet_rr; |
159 | 159 | struct batadv_icmp_header *icmp_header; | |
160 | struct batadv_orig_node *orig_node = NULL; | 160 | struct batadv_orig_node *orig_node = NULL; |
161 | struct batadv_neigh_node *neigh_node = NULL; | 161 | struct batadv_neigh_node *neigh_node = NULL; |
162 | size_t packet_len = sizeof(struct batadv_icmp_packet); | 162 | size_t packet_len = sizeof(struct batadv_icmp_packet); |
163 | 163 | ||
164 | if (len < sizeof(struct batadv_icmp_packet)) { | 164 | if (len < sizeof(struct batadv_icmp_header)) { |
165 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | 165 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, |
166 | "Error - can't send packet from char device: invalid packet size\n"); | 166 | "Error - can't send packet from char device: invalid packet size\n"); |
167 | return -EINVAL; | 167 | return -EINVAL; |
@@ -174,8 +174,10 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, | |||
174 | goto out; | 174 | goto out; |
175 | } | 175 | } |
176 | 176 | ||
177 | if (len >= sizeof(struct batadv_icmp_packet_rr)) | 177 | if (len >= BATADV_ICMP_MAX_PACKET_SIZE) |
178 | packet_len = sizeof(struct batadv_icmp_packet_rr); | 178 | packet_len = BATADV_ICMP_MAX_PACKET_SIZE; |
179 | else | ||
180 | packet_len = len; | ||
179 | 181 | ||
180 | skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN); | 182 | skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN); |
181 | if (!skb) { | 183 | if (!skb) { |
@@ -185,67 +187,78 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, | |||
185 | 187 | ||
186 | skb->priority = TC_PRIO_CONTROL; | 188 | skb->priority = TC_PRIO_CONTROL; |
187 | skb_reserve(skb, ETH_HLEN); | 189 | skb_reserve(skb, ETH_HLEN); |
188 | icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len); | 190 | icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len); |
189 | 191 | ||
190 | if (copy_from_user(icmp_packet, buff, packet_len)) { | 192 | if (copy_from_user(icmp_header, buff, packet_len)) { |
191 | len = -EFAULT; | 193 | len = -EFAULT; |
192 | goto free_skb; | 194 | goto free_skb; |
193 | } | 195 | } |
194 | 196 | ||
195 | if (icmp_packet->icmph.header.packet_type != BATADV_ICMP) { | 197 | if (icmp_header->header.packet_type != BATADV_ICMP) { |
196 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | 198 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, |
197 | "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); | 199 | "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); |
198 | len = -EINVAL; | 200 | len = -EINVAL; |
199 | goto free_skb; | 201 | goto free_skb; |
200 | } | 202 | } |
201 | 203 | ||
202 | if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) { | 204 | switch (icmp_header->msg_type) { |
205 | case BATADV_ECHO_REQUEST: | ||
206 | if (len < sizeof(struct batadv_icmp_packet)) { | ||
207 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | ||
208 | "Error - can't send packet from char device: invalid packet size\n"); | ||
209 | len = -EINVAL; | ||
210 | goto free_skb; | ||
211 | } | ||
212 | |||
213 | if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) | ||
214 | goto dst_unreach; | ||
215 | |||
216 | orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst); | ||
217 | if (!orig_node) | ||
218 | goto dst_unreach; | ||
219 | |||
220 | neigh_node = batadv_orig_node_get_router(orig_node); | ||
221 | if (!neigh_node) | ||
222 | goto dst_unreach; | ||
223 | |||
224 | if (!neigh_node->if_incoming) | ||
225 | goto dst_unreach; | ||
226 | |||
227 | if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) | ||
228 | goto dst_unreach; | ||
229 | |||
230 | icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header; | ||
231 | if (packet_len == sizeof(*icmp_packet_rr)) | ||
232 | memcpy(icmp_packet_rr->rr, | ||
233 | neigh_node->if_incoming->net_dev->dev_addr, | ||
234 | ETH_ALEN); | ||
235 | |||
236 | break; | ||
237 | default: | ||
203 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | 238 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, |
204 | "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n"); | 239 | "Error - can't send packet from char device: got unknown message type\n"); |
205 | len = -EINVAL; | 240 | len = -EINVAL; |
206 | goto free_skb; | 241 | goto free_skb; |
207 | } | 242 | } |
208 | 243 | ||
209 | icmp_packet->icmph.uid = socket_client->index; | 244 | icmp_header->uid = socket_client->index; |
210 | 245 | ||
211 | if (icmp_packet->icmph.header.version != BATADV_COMPAT_VERSION) { | 246 | if (icmp_header->header.version != BATADV_COMPAT_VERSION) { |
212 | icmp_packet->icmph.msg_type = BATADV_PARAMETER_PROBLEM; | 247 | icmp_header->msg_type = BATADV_PARAMETER_PROBLEM; |
213 | icmp_packet->icmph.header.version = BATADV_COMPAT_VERSION; | 248 | icmp_header->header.version = BATADV_COMPAT_VERSION; |
214 | batadv_socket_add_packet(socket_client, icmp_packet, | 249 | batadv_socket_add_packet(socket_client, icmp_header, |
215 | packet_len); | 250 | packet_len); |
216 | goto free_skb; | 251 | goto free_skb; |
217 | } | 252 | } |
218 | 253 | ||
219 | if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) | 254 | memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN); |
220 | goto dst_unreach; | ||
221 | |||
222 | orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.dst); | ||
223 | if (!orig_node) | ||
224 | goto dst_unreach; | ||
225 | |||
226 | neigh_node = batadv_orig_node_get_router(orig_node); | ||
227 | if (!neigh_node) | ||
228 | goto dst_unreach; | ||
229 | |||
230 | if (!neigh_node->if_incoming) | ||
231 | goto dst_unreach; | ||
232 | |||
233 | if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) | ||
234 | goto dst_unreach; | ||
235 | |||
236 | memcpy(icmp_packet->icmph.orig, | ||
237 | primary_if->net_dev->dev_addr, ETH_ALEN); | ||
238 | |||
239 | if (packet_len == sizeof(struct batadv_icmp_packet_rr)) | ||
240 | memcpy(icmp_packet->rr, | ||
241 | neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN); | ||
242 | 255 | ||
243 | batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); | 256 | batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); |
244 | goto out; | 257 | goto out; |
245 | 258 | ||
246 | dst_unreach: | 259 | dst_unreach: |
247 | icmp_packet->icmph.msg_type = BATADV_DESTINATION_UNREACHABLE; | 260 | icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE; |
248 | batadv_socket_add_packet(socket_client, icmp_packet, packet_len); | 261 | batadv_socket_add_packet(socket_client, icmp_header, packet_len); |
249 | free_skb: | 262 | free_skb: |
250 | kfree_skb(skb); | 263 | kfree_skb(skb); |
251 | out: | 264 | out: |
@@ -298,27 +311,40 @@ err: | |||
298 | return -ENOMEM; | 311 | return -ENOMEM; |
299 | } | 312 | } |
300 | 313 | ||
314 | /** | ||
315 | * batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace | ||
316 | * on an icmp socket. | ||
317 | * @socket_client: the socket this packet belongs to | ||
318 | * @icmph: pointer to the header of the icmp packet | ||
319 | * @icmp_len: total length of the icmp packet | ||
320 | */ | ||
301 | static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, | 321 | static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, |
302 | struct batadv_icmp_packet_rr *icmp_packet, | 322 | struct batadv_icmp_header *icmph, |
303 | size_t icmp_len) | 323 | size_t icmp_len) |
304 | { | 324 | { |
305 | struct batadv_socket_packet *socket_packet; | 325 | struct batadv_socket_packet *socket_packet; |
326 | size_t len; | ||
306 | 327 | ||
307 | socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); | 328 | socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); |
308 | 329 | ||
309 | if (!socket_packet) | 330 | if (!socket_packet) |
310 | return; | 331 | return; |
311 | 332 | ||
333 | len = icmp_len; | ||
334 | /* check the maximum length before filling the buffer */ | ||
335 | if (len > sizeof(socket_packet->icmp_packet)) | ||
336 | len = sizeof(socket_packet->icmp_packet); | ||
337 | |||
312 | INIT_LIST_HEAD(&socket_packet->list); | 338 | INIT_LIST_HEAD(&socket_packet->list); |
313 | memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len); | 339 | memcpy(&socket_packet->icmp_packet, icmph, len); |
314 | socket_packet->icmp_len = icmp_len; | 340 | socket_packet->icmp_len = len; |
315 | 341 | ||
316 | spin_lock_bh(&socket_client->lock); | 342 | spin_lock_bh(&socket_client->lock); |
317 | 343 | ||
318 | /* while waiting for the lock the socket_client could have been | 344 | /* while waiting for the lock the socket_client could have been |
319 | * deleted | 345 | * deleted |
320 | */ | 346 | */ |
321 | if (!batadv_socket_client_hash[icmp_packet->icmph.uid]) { | 347 | if (!batadv_socket_client_hash[icmph->uid]) { |
322 | spin_unlock_bh(&socket_client->lock); | 348 | spin_unlock_bh(&socket_client->lock); |
323 | kfree(socket_packet); | 349 | kfree(socket_packet); |
324 | return; | 350 | return; |
@@ -342,12 +368,18 @@ static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, | |||
342 | wake_up(&socket_client->queue_wait); | 368 | wake_up(&socket_client->queue_wait); |
343 | } | 369 | } |
344 | 370 | ||
345 | void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet, | 371 | /** |
372 | * batadv_socket_receive_packet - schedule an icmp packet to be received | ||
373 | * locally and sent to userspace. | ||
374 | * @icmph: pointer to the header of the icmp packet | ||
375 | * @icmp_len: total length of the icmp packet | ||
376 | */ | ||
377 | void batadv_socket_receive_packet(struct batadv_icmp_header *icmph, | ||
346 | size_t icmp_len) | 378 | size_t icmp_len) |
347 | { | 379 | { |
348 | struct batadv_socket_client *hash; | 380 | struct batadv_socket_client *hash; |
349 | 381 | ||
350 | hash = batadv_socket_client_hash[icmp_packet->icmph.uid]; | 382 | hash = batadv_socket_client_hash[icmph->uid]; |
351 | if (hash) | 383 | if (hash) |
352 | batadv_socket_add_packet(hash, icmp_packet, icmp_len); | 384 | batadv_socket_add_packet(hash, icmph, icmp_len); |
353 | } | 385 | } |