Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[SCTP]: Handle address add/delete events in a more efficient way.

Currently in SCTP, we maintain a local address list by rebuilding the whole
list from the device list whenever we get a address add/delete event.

This patch fixes it by only adding/deleting the address for which we
receive the event.

Also removed the sctp_local_addr_lock() which is no longer needed as we
now use list_for_each_safe() to traverse this list. This fixes the bugs
in sctp_copy_laddrs_xxx() routines where we do copy_to_user() while
holding this lock.

Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Sridhar Samudrala and committed by
David S. Miller
29c7cf96 6931ba7c

+81 -66
+2 -4
include/net/sctp/structs.h
··· 201 201 struct sctp_bind_hashbucket *port_hashtable; 202 202 203 203 /* This is the global local address list. 204 - * We actively maintain this complete list of interfaces on 205 - * the system by catching routing events. 204 + * We actively maintain this complete list of addresses on 205 + * the system by catching address add/delete events. 206 206 * 207 207 * It is a list of sctp_sockaddr_entry. 208 208 */ 209 209 struct list_head local_addr_list; 210 - spinlock_t local_addr_lock; 211 210 212 211 /* Flag to indicate if addip is enabled. */ 213 212 int addip_enable; ··· 242 243 #define sctp_port_alloc_lock (sctp_globals.port_alloc_lock) 243 244 #define sctp_port_hashtable (sctp_globals.port_hashtable) 244 245 #define sctp_local_addr_list (sctp_globals.local_addr_list) 245 - #define sctp_local_addr_lock (sctp_globals.local_addr_lock) 246 246 #define sctp_addip_enable (sctp_globals.addip_enable) 247 247 #define sctp_prsctp_enable (sctp_globals.prsctp_enable) 248 248
+37 -1
net/sctp/ipv6.c
··· 78 78 79 79 #include <asm/uaccess.h> 80 80 81 + /* Event handler for inet6 address addition/deletion events. */ 82 + int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, 83 + void *ptr) 84 + { 85 + struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; 86 + struct sctp_sockaddr_entry *addr; 87 + struct list_head *pos, *temp; 88 + 89 + switch (ev) { 90 + case NETDEV_UP: 91 + addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); 92 + if (addr) { 93 + addr->a.v6.sin6_family = AF_INET6; 94 + addr->a.v6.sin6_port = 0; 95 + memcpy(&addr->a.v6.sin6_addr, &ifa->addr, 96 + sizeof(struct in6_addr)); 97 + addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; 98 + list_add_tail(&addr->list, &sctp_local_addr_list); 99 + } 100 + break; 101 + case NETDEV_DOWN: 102 + list_for_each_safe(pos, temp, &sctp_local_addr_list) { 103 + addr = list_entry(pos, struct sctp_sockaddr_entry, list); 104 + if (ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { 105 + list_del(pos); 106 + kfree(addr); 107 + break; 108 + } 109 + } 110 + 111 + break; 112 + } 113 + 114 + return NOTIFY_DONE; 115 + } 116 + 81 117 static struct notifier_block sctp_inet6addr_notifier = { 82 - .notifier_call = sctp_inetaddr_event, 118 + .notifier_call = sctp_inet6addr_event, 83 119 }; 84 120 85 121 /* ICMP error handler. */
+32 -37
net/sctp/protocol.c
··· 163 163 /* Extract our IP addresses from the system and stash them in the 164 164 * protocol structure. 165 165 */ 166 - static void __sctp_get_local_addr_list(void) 166 + static void sctp_get_local_addr_list(void) 167 167 { 168 168 struct net_device *dev; 169 169 struct list_head *pos; ··· 179 179 read_unlock(&dev_base_lock); 180 180 } 181 181 182 - static void sctp_get_local_addr_list(void) 183 - { 184 - unsigned long flags; 185 - 186 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 187 - __sctp_get_local_addr_list(); 188 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 189 - } 190 - 191 182 /* Free the existing local addresses. */ 192 - static void __sctp_free_local_addr_list(void) 183 + static void sctp_free_local_addr_list(void) 193 184 { 194 185 struct sctp_sockaddr_entry *addr; 195 186 struct list_head *pos, *temp; ··· 192 201 } 193 202 } 194 203 195 - /* Free the existing local addresses. */ 196 - static void sctp_free_local_addr_list(void) 197 - { 198 - unsigned long flags; 199 - 200 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 201 - __sctp_free_local_addr_list(); 202 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 203 - } 204 - 205 204 /* Copy the local addresses which are valid for 'scope' into 'bp'. */ 206 205 int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, 207 206 gfp_t gfp, int copy_flags) 208 207 { 209 208 struct sctp_sockaddr_entry *addr; 210 209 int error = 0; 211 - struct list_head *pos; 212 - unsigned long flags; 210 + struct list_head *pos, *temp; 213 211 214 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 215 - list_for_each(pos, &sctp_local_addr_list) { 212 + list_for_each_safe(pos, temp, &sctp_local_addr_list) { 216 213 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 217 214 if (sctp_in_scope(&addr->a, scope)) { 218 215 /* Now that the address is in scope, check to see if ··· 221 242 } 222 243 223 244 end_copy: 224 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 225 245 return error; 226 246 } 227 247 ··· 600 622 seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); 601 623 } 602 624 603 - /* Event handler for inet address addition/deletion events. 604 - * Basically, whenever there is an event, we re-build our local address list. 605 - */ 625 + /* Event handler for inet address addition/deletion events. */ 606 626 int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, 607 627 void *ptr) 608 628 { 609 - unsigned long flags; 629 + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; 630 + struct sctp_sockaddr_entry *addr; 631 + struct list_head *pos, *temp; 610 632 611 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 612 - __sctp_free_local_addr_list(); 613 - __sctp_get_local_addr_list(); 614 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 633 + switch (ev) { 634 + case NETDEV_UP: 635 + addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); 636 + if (addr) { 637 + addr->a.v4.sin_family = AF_INET; 638 + addr->a.v4.sin_port = 0; 639 + addr->a.v4.sin_addr.s_addr = ifa->ifa_local; 640 + list_add_tail(&addr->list, &sctp_local_addr_list); 641 + } 642 + break; 643 + case NETDEV_DOWN: 644 + list_for_each_safe(pos, temp, &sctp_local_addr_list) { 645 + addr = list_entry(pos, struct sctp_sockaddr_entry, list); 646 + if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { 647 + list_del(pos); 648 + kfree(addr); 649 + break; 650 + } 651 + } 652 + 653 + break; 654 + } 615 655 616 656 return NOTIFY_DONE; 617 657 } ··· 1168 1172 1169 1173 /* Initialize the local address list. */ 1170 1174 INIT_LIST_HEAD(&sctp_local_addr_list); 1171 - spin_lock_init(&sctp_local_addr_lock); 1175 + 1176 + sctp_get_local_addr_list(); 1172 1177 1173 1178 /* Register notifier for inet address additions/deletions. */ 1174 1179 register_inetaddr_notifier(&sctp_inetaddr_notifier); 1175 - 1176 - sctp_get_local_addr_list(); 1177 1180 1178 1181 __unsafe(THIS_MODULE); 1179 1182 status = 0;
+10 -24
net/sctp/socket.c
··· 3821 3821 sctp_assoc_t id; 3822 3822 struct sctp_bind_addr *bp; 3823 3823 struct sctp_association *asoc; 3824 - struct list_head *pos; 3824 + struct list_head *pos, *temp; 3825 3825 struct sctp_sockaddr_entry *addr; 3826 3826 rwlock_t *addr_lock; 3827 - unsigned long flags; 3828 3827 int cnt = 0; 3829 3828 3830 3829 if (len != sizeof(sctp_assoc_t)) ··· 3858 3859 addr = list_entry(bp->address_list.next, 3859 3860 struct sctp_sockaddr_entry, list); 3860 3861 if (sctp_is_any(&addr->a)) { 3861 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 3862 - list_for_each(pos, &sctp_local_addr_list) { 3862 + list_for_each_safe(pos, temp, &sctp_local_addr_list) { 3863 3863 addr = list_entry(pos, 3864 3864 struct sctp_sockaddr_entry, 3865 3865 list); ··· 3867 3869 continue; 3868 3870 cnt++; 3869 3871 } 3870 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, 3871 - flags); 3872 3872 } else { 3873 3873 cnt = 1; 3874 3874 } ··· 3888 3892 static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, 3889 3893 void __user *to) 3890 3894 { 3891 - struct list_head *pos; 3895 + struct list_head *pos, *next; 3892 3896 struct sctp_sockaddr_entry *addr; 3893 - unsigned long flags; 3894 3897 union sctp_addr temp; 3895 3898 int cnt = 0; 3896 3899 int addrlen; 3897 3900 3898 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 3899 - list_for_each(pos, &sctp_local_addr_list) { 3901 + list_for_each_safe(pos, next, &sctp_local_addr_list) { 3900 3902 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 3901 3903 if ((PF_INET == sk->sk_family) && 3902 3904 (AF_INET6 == addr->a.sa.sa_family)) ··· 3903 3909 sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), 3904 3910 &temp); 3905 3911 addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; 3906 - if (copy_to_user(to, &temp, addrlen)) { 3907 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, 3908 - flags); 3912 + if (copy_to_user(to, &temp, addrlen)) 3909 3913 return -EFAULT; 3910 - } 3914 + 3911 3915 to += addrlen; 3912 3916 cnt ++; 3913 3917 if (cnt >= max_addrs) break; 3914 3918 } 3915 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 3916 3919 3917 3920 return cnt; 3918 3921 } ··· 3917 3926 static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, 3918 3927 void __user **to, size_t space_left) 3919 3928 { 3920 - struct list_head *pos; 3929 + struct list_head *pos, *next; 3921 3930 struct sctp_sockaddr_entry *addr; 3922 - unsigned long flags; 3923 3931 union sctp_addr temp; 3924 3932 int cnt = 0; 3925 3933 int addrlen; 3926 3934 3927 - sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); 3928 - list_for_each(pos, &sctp_local_addr_list) { 3935 + list_for_each_safe(pos, next, &sctp_local_addr_list) { 3929 3936 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 3930 3937 if ((PF_INET == sk->sk_family) && 3931 3938 (AF_INET6 == addr->a.sa.sa_family)) ··· 3934 3945 addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; 3935 3946 if(space_left<addrlen) 3936 3947 return -ENOMEM; 3937 - if (copy_to_user(*to, &temp, addrlen)) { 3938 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, 3939 - flags); 3948 + if (copy_to_user(*to, &temp, addrlen)) 3940 3949 return -EFAULT; 3941 - } 3950 + 3942 3951 *to += addrlen; 3943 3952 cnt ++; 3944 3953 space_left -= addrlen; 3945 3954 } 3946 - sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); 3947 3955 3948 3956 return cnt; 3949 3957 }