c02a61ca3dfe4023d2b9b1486f8cd399bc97b5f5
[linux-flexiantxendom0-3.2.10.git] / net / ipv6 / mobile_ip6 / prefix.c
1 /**
2  * Prefix solicitation and advertisement
3  *
4  * Authors:
5  * Jaakko Laine <medved@iki.fi>
6  *
7  * $Id: s.prefix.c 1.30 03/04/10 13:09:54+03:00 anttit@jon.mipl.mediapoli.com $
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version
12  * 2 of the License, or (at your option) any later version.
13  */
14
15 #include <linux/kernel.h>
16 #include <linux/types.h>
17 #include <linux/config.h>
18 #include <linux/icmpv6.h>
19 #include <linux/net.h>
20 #include <linux/spinlock.h>
21 #include <linux/timer.h>
22 #include <linux/netdevice.h>
23 #include <net/ipv6.h>
24 #include <net/addrconf.h>
25 #include <net/ip6_route.h>
26 #include <net/mipv6.h>
27
28 #include "mipv6_icmp.h"
29 #include "debug.h"
30 #include "sortedlist.h"
31 #include "prefix.h"
32 #include "config.h"
33
34 #define INFINITY 0xffffffff
35
36 struct timer_list pfx_timer;
37
38 struct list_head pfx_list;
39 rwlock_t pfx_list_lock = RW_LOCK_UNLOCKED;
40
41 int compare_pfx_list_entry(const void *data1, const void *data2,
42                            int datalen)
43 {
44         struct pfx_list_entry *e1 = (struct pfx_list_entry *) data1;
45         struct pfx_list_entry *e2 = (struct pfx_list_entry *) data2;
46
47         return ((ipv6_addr_cmp(&e1->daddr, &e2->daddr) == 0)
48                 && (e2->ifindex == -1 || e1->ifindex == e2->ifindex));
49 }
50
51 /**
52  * mipv6_pfx_cancel_send - cancel pending items to daddr from saddr
53  * @daddr: Destination address
54  * @ifindex: pending items on this interface will be canceled
55  *
56  * if ifindex == -1, all items to daddr will be removed
57  */
58 void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex)
59 {
60         unsigned long tmp;
61         struct pfx_list_entry entry;
62
63         DEBUG_FUNC();
64
65         /* We'll just be comparing these parts... */
66         memcpy(&entry.daddr, daddr, sizeof(struct in6_addr));
67         entry.ifindex = ifindex;
68
69         write_lock_bh(&pfx_list_lock);
70
71         while (mipv6_slist_del_item(&pfx_list, &entry,
72                                     compare_pfx_list_entry) == 0)
73                 ;
74
75         if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
76                 mod_timer(&pfx_timer, tmp);
77
78         write_unlock_bh(&pfx_list_lock);
79 }
80
81 /**
82  * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to
83  * @daddr: address of HA
84  * @saddr: our address to use as source address
85  * @ifindex: interface index
86  */
87 void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr,
88                       int ifindex)
89 {
90         unsigned long tmp;
91         struct pfx_list_entry entry;
92
93         DEBUG_FUNC();
94
95         memcpy(&entry.daddr, daddr, sizeof(struct in6_addr));
96         memcpy(&entry.saddr, saddr, sizeof(struct in6_addr));
97         entry.retries = 0;
98         entry.ifindex = ifindex;
99
100         write_lock_bh(&pfx_list_lock);
101         if (mipv6_slist_modify(&pfx_list, &entry, sizeof(struct pfx_list_entry),
102                                jiffies + INITIAL_SOLICIT_TIMER * HZ,
103                                compare_pfx_list_entry))
104                 DEBUG(DBG_WARNING, "Cannot add new HA to pfx list");
105
106         if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
107                 mod_timer(&pfx_timer, tmp);
108         write_unlock_bh(&pfx_list_lock);
109 }
110
111 int mipv6_pfx_add_home(int ifindex, struct in6_addr *saddr, 
112                        struct in6_addr *daddr, unsigned long min_expire)
113 {
114         unsigned long tmp;
115
116         write_lock(&pfx_list_lock);
117
118         if (min_expire != INFINITY) {
119                 unsigned long expire;
120                 struct pfx_list_entry entry;
121                 
122                 memcpy(&entry.daddr, saddr, sizeof(struct in6_addr));
123                 memcpy(&entry.saddr, daddr, sizeof(struct in6_addr));
124                 entry.retries = 0;
125                 entry.ifindex = ifindex;
126
127                 /* This is against the draft, but we need to set
128                  * a minimum interval for a prefix solicitation.
129                  * Otherwise a prefix solicitation storm will
130                  * result if valid lifetime of the prefix is
131                  * smaller than MAX_PFX_ADV_DELAY
132                  */
133                 min_expire -= MAX_PFX_ADV_DELAY;
134                 min_expire = min_expire < MIN_PFX_SOL_DELAY ? MIN_PFX_SOL_DELAY : min_expire;
135
136                 expire = jiffies + min_expire * HZ;
137
138                 if (mipv6_slist_modify(&pfx_list, &entry,
139                                        sizeof(struct pfx_list_entry),
140                                        expire,
141                                        compare_pfx_list_entry) != 0)
142                         DEBUG(DBG_WARNING, "Cannot add new entry to pfx_list");
143         }
144
145         if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
146                 mod_timer(&pfx_timer, tmp);
147
148         write_unlock(&pfx_list_lock);
149
150         return 0;
151 }
152
153 /**
154  * set_ha_pfx_list - manipulate pfx_list for HA when timer goes off
155  * @entry: pfx_list_entry that is due
156  */
157 static void set_ha_pfx_list(struct pfx_list_entry *entry)
158 {
159 }
160
161 /**
162  * set_mn_pfx_list - manipulate pfx_list for MN when timer goes off
163  * @entry: pfx_list_entry that is due
164  */
165 static void set_mn_pfx_list(struct pfx_list_entry *entry)
166 {
167 }
168
169 /**
170  * pfx_timer_handler - general timer handler
171  * @dummy: dummy
172  *
173  * calls set_ha_pfx_list and set_mn_pfx_list to do the thing when
174  * a timer goes off
175  */
176 static void pfx_timer_handler(unsigned long dummy)
177 {
178         unsigned long tmp;
179         struct pfx_list_entry *entry;
180
181         DEBUG_FUNC();
182
183         write_lock(&pfx_list_lock);
184         if (!(entry = mipv6_slist_get_first(&pfx_list)))
185                 goto out;
186
187         if (mip6node_cnf.capabilities & CAP_HA)
188                 set_ha_pfx_list(entry);
189         if (mip6node_cnf.capabilities & CAP_MN)
190                 set_mn_pfx_list(entry);
191         if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
192                 mod_timer(&pfx_timer, tmp);
193
194  out:
195         write_unlock(&pfx_list_lock);
196 }
197
198 int mipv6_initialize_pfx_icmpv6(void)
199 {
200         INIT_LIST_HEAD(&pfx_list);
201
202         init_timer(&pfx_timer);
203         pfx_timer.function = pfx_timer_handler;
204
205         return 0;
206 }
207
208 void mipv6_shutdown_pfx_icmpv6(void)
209 {
210         struct prefix_info *tmp;
211
212         if (timer_pending(&pfx_timer))
213                 del_timer(&pfx_timer);
214
215         write_lock_bh(&pfx_list_lock);
216         while ((tmp = mipv6_slist_del_first(&pfx_list)))
217                 kfree(tmp);
218         write_unlock_bh(&pfx_list_lock);
219 }
220
221 EXPORT_SYMBOL(mipv6_initialize_pfx_icmpv6);
222 EXPORT_SYMBOL(mipv6_pfx_add_home);
223 EXPORT_SYMBOL(mipv6_shutdown_pfx_icmpv6);
224 EXPORT_SYMBOL(compare_pfx_list_entry);
225 EXPORT_SYMBOL(pfx_list);
226 EXPORT_SYMBOL(pfx_timer);
227 EXPORT_SYMBOL(mipv6_pfx_cancel_send);
228 EXPORT_SYMBOL(pfx_list_lock);