1686cdd19SJun-ichiro itojun Hagino /* $FreeBSD$ */ 233841545SHajimu UMEMOTO /* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */ 3686cdd19SJun-ichiro itojun Hagino 4686cdd19SJun-ichiro itojun Hagino /* 5686cdd19SJun-ichiro itojun Hagino * Copyright (C) 2000 WIDE Project. 6686cdd19SJun-ichiro itojun Hagino * All rights reserved. 7686cdd19SJun-ichiro itojun Hagino * 8686cdd19SJun-ichiro itojun Hagino * Redistribution and use in source and binary forms, with or without 9686cdd19SJun-ichiro itojun Hagino * modification, are permitted provided that the following conditions 10686cdd19SJun-ichiro itojun Hagino * are met: 11686cdd19SJun-ichiro itojun Hagino * 1. Redistributions of source code must retain the above copyright 12686cdd19SJun-ichiro itojun Hagino * notice, this list of conditions and the following disclaimer. 13686cdd19SJun-ichiro itojun Hagino * 2. Redistributions in binary form must reproduce the above copyright 14686cdd19SJun-ichiro itojun Hagino * notice, this list of conditions and the following disclaimer in the 15686cdd19SJun-ichiro itojun Hagino * documentation and/or other materials provided with the distribution. 16686cdd19SJun-ichiro itojun Hagino * 3. Neither the name of the project nor the names of its contributors 17686cdd19SJun-ichiro itojun Hagino * may be used to endorse or promote products derived from this software 18686cdd19SJun-ichiro itojun Hagino * without specific prior written permission. 19686cdd19SJun-ichiro itojun Hagino * 20686cdd19SJun-ichiro itojun Hagino * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21686cdd19SJun-ichiro itojun Hagino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22686cdd19SJun-ichiro itojun Hagino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23686cdd19SJun-ichiro itojun Hagino * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24686cdd19SJun-ichiro itojun Hagino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25686cdd19SJun-ichiro itojun Hagino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26686cdd19SJun-ichiro itojun Hagino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27686cdd19SJun-ichiro itojun Hagino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28686cdd19SJun-ichiro itojun Hagino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29686cdd19SJun-ichiro itojun Hagino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30686cdd19SJun-ichiro itojun Hagino * SUCH DAMAGE. 31686cdd19SJun-ichiro itojun Hagino */ 32686cdd19SJun-ichiro itojun Hagino 33686cdd19SJun-ichiro itojun Hagino #include <sys/param.h> 34686cdd19SJun-ichiro itojun Hagino #include <sys/malloc.h> 35686cdd19SJun-ichiro itojun Hagino #include <sys/mbuf.h> 36686cdd19SJun-ichiro itojun Hagino #include <sys/socket.h> 37686cdd19SJun-ichiro itojun Hagino #include <sys/systm.h> 3833841545SHajimu UMEMOTO #include <sys/queue.h> 39686cdd19SJun-ichiro itojun Hagino 40686cdd19SJun-ichiro itojun Hagino #include <net/route.h> 41686cdd19SJun-ichiro itojun Hagino #include <net/if.h> 42686cdd19SJun-ichiro itojun Hagino 43686cdd19SJun-ichiro itojun Hagino #include <netinet/in.h> 44686cdd19SJun-ichiro itojun Hagino 45686cdd19SJun-ichiro itojun Hagino #include <netinet6/in6_var.h> 46686cdd19SJun-ichiro itojun Hagino #include <netinet6/scope6_var.h> 47686cdd19SJun-ichiro itojun Hagino 48686cdd19SJun-ichiro itojun Hagino struct scope6_id { 49686cdd19SJun-ichiro itojun Hagino /* 50686cdd19SJun-ichiro itojun Hagino * 16 is correspondent to 4bit multicast scope field. 51686cdd19SJun-ichiro itojun Hagino * i.e. from node-local to global with some reserved/unassigned types. 52686cdd19SJun-ichiro itojun Hagino */ 53686cdd19SJun-ichiro itojun Hagino u_int32_t s6id_list[16]; 54686cdd19SJun-ichiro itojun Hagino }; 55686cdd19SJun-ichiro itojun Hagino static size_t if_indexlim = 8; 56686cdd19SJun-ichiro itojun Hagino struct scope6_id *scope6_ids = NULL; 57686cdd19SJun-ichiro itojun Hagino 58686cdd19SJun-ichiro itojun Hagino void 59686cdd19SJun-ichiro itojun Hagino scope6_ifattach(ifp) 60686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 61686cdd19SJun-ichiro itojun Hagino { 62686cdd19SJun-ichiro itojun Hagino int s = splnet(); 63686cdd19SJun-ichiro itojun Hagino 64686cdd19SJun-ichiro itojun Hagino /* 65686cdd19SJun-ichiro itojun Hagino * We have some arrays that should be indexed by if_index. 66686cdd19SJun-ichiro itojun Hagino * since if_index will grow dynamically, they should grow too. 67686cdd19SJun-ichiro itojun Hagino */ 68686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL || if_index >= if_indexlim) { 69686cdd19SJun-ichiro itojun Hagino size_t n; 70686cdd19SJun-ichiro itojun Hagino caddr_t q; 71686cdd19SJun-ichiro itojun Hagino 72686cdd19SJun-ichiro itojun Hagino while (if_index >= if_indexlim) 73686cdd19SJun-ichiro itojun Hagino if_indexlim <<= 1; 74686cdd19SJun-ichiro itojun Hagino 75686cdd19SJun-ichiro itojun Hagino /* grow scope index array */ 76686cdd19SJun-ichiro itojun Hagino n = if_indexlim * sizeof(struct scope6_id); 77686cdd19SJun-ichiro itojun Hagino /* XXX: need new malloc type? */ 78686cdd19SJun-ichiro itojun Hagino q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 79686cdd19SJun-ichiro itojun Hagino bzero(q, n); 80686cdd19SJun-ichiro itojun Hagino if (scope6_ids) { 81686cdd19SJun-ichiro itojun Hagino bcopy((caddr_t)scope6_ids, q, n/2); 82686cdd19SJun-ichiro itojun Hagino free((caddr_t)scope6_ids, M_IFADDR); 83686cdd19SJun-ichiro itojun Hagino } 84686cdd19SJun-ichiro itojun Hagino scope6_ids = (struct scope6_id *)q; 85686cdd19SJun-ichiro itojun Hagino } 86686cdd19SJun-ichiro itojun Hagino 87686cdd19SJun-ichiro itojun Hagino #define SID scope6_ids[ifp->if_index] 88686cdd19SJun-ichiro itojun Hagino 89686cdd19SJun-ichiro itojun Hagino /* don't initialize if called twice */ 90686cdd19SJun-ichiro itojun Hagino if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { 91686cdd19SJun-ichiro itojun Hagino splx(s); 92686cdd19SJun-ichiro itojun Hagino return; 93686cdd19SJun-ichiro itojun Hagino } 94686cdd19SJun-ichiro itojun Hagino 95686cdd19SJun-ichiro itojun Hagino /* 96686cdd19SJun-ichiro itojun Hagino * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 97686cdd19SJun-ichiro itojun Hagino * Should we rather hardcode here? 98686cdd19SJun-ichiro itojun Hagino */ 99686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 100686cdd19SJun-ichiro itojun Hagino #ifdef MULTI_SCOPE 101686cdd19SJun-ichiro itojun Hagino /* by default, we don't care about scope boundary for these scopes. */ 102686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 103686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 104686cdd19SJun-ichiro itojun Hagino #endif 105686cdd19SJun-ichiro itojun Hagino #undef SID 106686cdd19SJun-ichiro itojun Hagino 107686cdd19SJun-ichiro itojun Hagino splx(s); 108686cdd19SJun-ichiro itojun Hagino } 109686cdd19SJun-ichiro itojun Hagino 110686cdd19SJun-ichiro itojun Hagino int 111686cdd19SJun-ichiro itojun Hagino scope6_set(ifp, idlist) 112686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 113686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 114686cdd19SJun-ichiro itojun Hagino { 115686cdd19SJun-ichiro itojun Hagino int i, s; 116686cdd19SJun-ichiro itojun Hagino int error = 0; 117686cdd19SJun-ichiro itojun Hagino 118686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 119686cdd19SJun-ichiro itojun Hagino return(EINVAL); 120686cdd19SJun-ichiro itojun Hagino 121686cdd19SJun-ichiro itojun Hagino /* 122686cdd19SJun-ichiro itojun Hagino * XXX: We need more consistency checks of the relationship among 123686cdd19SJun-ichiro itojun Hagino * scopes (e.g. an organization should be larger than a site). 124686cdd19SJun-ichiro itojun Hagino */ 125686cdd19SJun-ichiro itojun Hagino 126686cdd19SJun-ichiro itojun Hagino /* 127686cdd19SJun-ichiro itojun Hagino * TODO(XXX): after setting, we should reflect the changes to 128686cdd19SJun-ichiro itojun Hagino * interface addresses, routing table entries, PCB entries... 129686cdd19SJun-ichiro itojun Hagino */ 130686cdd19SJun-ichiro itojun Hagino 131686cdd19SJun-ichiro itojun Hagino s = splnet(); 132686cdd19SJun-ichiro itojun Hagino 133686cdd19SJun-ichiro itojun Hagino for (i = 0; i < 16; i++) { 134686cdd19SJun-ichiro itojun Hagino if (idlist[i] && 135686cdd19SJun-ichiro itojun Hagino idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 136686cdd19SJun-ichiro itojun Hagino if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 137686cdd19SJun-ichiro itojun Hagino idlist[i] > if_index) { 138686cdd19SJun-ichiro itojun Hagino /* 139686cdd19SJun-ichiro itojun Hagino * XXX: theoretically, there should be no 140686cdd19SJun-ichiro itojun Hagino * relationship between link IDs and interface 141686cdd19SJun-ichiro itojun Hagino * IDs, but we check the consistency for 142686cdd19SJun-ichiro itojun Hagino * safety in later use. 143686cdd19SJun-ichiro itojun Hagino */ 144686cdd19SJun-ichiro itojun Hagino splx(s); 145686cdd19SJun-ichiro itojun Hagino return(EINVAL); 146686cdd19SJun-ichiro itojun Hagino } 147686cdd19SJun-ichiro itojun Hagino 148686cdd19SJun-ichiro itojun Hagino /* 149686cdd19SJun-ichiro itojun Hagino * XXX: we must need lots of work in this case, 150686cdd19SJun-ichiro itojun Hagino * but we simply set the new value in this initial 151686cdd19SJun-ichiro itojun Hagino * implementation. 152686cdd19SJun-ichiro itojun Hagino */ 153686cdd19SJun-ichiro itojun Hagino scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 154686cdd19SJun-ichiro itojun Hagino } 155686cdd19SJun-ichiro itojun Hagino } 156686cdd19SJun-ichiro itojun Hagino splx(s); 157686cdd19SJun-ichiro itojun Hagino 158686cdd19SJun-ichiro itojun Hagino return(error); 159686cdd19SJun-ichiro itojun Hagino } 160686cdd19SJun-ichiro itojun Hagino 161686cdd19SJun-ichiro itojun Hagino int 162686cdd19SJun-ichiro itojun Hagino scope6_get(ifp, idlist) 163686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 164686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 165686cdd19SJun-ichiro itojun Hagino { 166686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 167686cdd19SJun-ichiro itojun Hagino return(EINVAL); 168686cdd19SJun-ichiro itojun Hagino 169686cdd19SJun-ichiro itojun Hagino bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 170686cdd19SJun-ichiro itojun Hagino sizeof(scope6_ids[ifp->if_index].s6id_list)); 171686cdd19SJun-ichiro itojun Hagino 172686cdd19SJun-ichiro itojun Hagino return(0); 173686cdd19SJun-ichiro itojun Hagino } 174686cdd19SJun-ichiro itojun Hagino 175686cdd19SJun-ichiro itojun Hagino 176686cdd19SJun-ichiro itojun Hagino /* 177686cdd19SJun-ichiro itojun Hagino * Get a scope of the address. Node-local, link-local, site-local or global. 178686cdd19SJun-ichiro itojun Hagino */ 179686cdd19SJun-ichiro itojun Hagino int 180686cdd19SJun-ichiro itojun Hagino in6_addrscope(addr) 181686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; 182686cdd19SJun-ichiro itojun Hagino { 183686cdd19SJun-ichiro itojun Hagino int scope; 184686cdd19SJun-ichiro itojun Hagino 185686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[0] == 0xfe) { 186686cdd19SJun-ichiro itojun Hagino scope = addr->s6_addr8[1] & 0xc0; 187686cdd19SJun-ichiro itojun Hagino 188686cdd19SJun-ichiro itojun Hagino switch (scope) { 189686cdd19SJun-ichiro itojun Hagino case 0x80: 190686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 191686cdd19SJun-ichiro itojun Hagino break; 192686cdd19SJun-ichiro itojun Hagino case 0xc0: 193686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_SITELOCAL; 194686cdd19SJun-ichiro itojun Hagino break; 195686cdd19SJun-ichiro itojun Hagino default: 196686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 197686cdd19SJun-ichiro itojun Hagino break; 198686cdd19SJun-ichiro itojun Hagino } 199686cdd19SJun-ichiro itojun Hagino } 200686cdd19SJun-ichiro itojun Hagino 201686cdd19SJun-ichiro itojun Hagino 202686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[0] == 0xff) { 203686cdd19SJun-ichiro itojun Hagino scope = addr->s6_addr8[1] & 0x0f; 204686cdd19SJun-ichiro itojun Hagino 205686cdd19SJun-ichiro itojun Hagino /* 206686cdd19SJun-ichiro itojun Hagino * due to other scope such as reserved, 207686cdd19SJun-ichiro itojun Hagino * return scope doesn't work. 208686cdd19SJun-ichiro itojun Hagino */ 209686cdd19SJun-ichiro itojun Hagino switch (scope) { 210686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_NODELOCAL: 211686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_NODELOCAL; 212686cdd19SJun-ichiro itojun Hagino break; 213686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_LINKLOCAL: 214686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 215686cdd19SJun-ichiro itojun Hagino break; 216686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_SITELOCAL: 217686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_SITELOCAL; 218686cdd19SJun-ichiro itojun Hagino break; 219686cdd19SJun-ichiro itojun Hagino default: 220686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; 221686cdd19SJun-ichiro itojun Hagino break; 222686cdd19SJun-ichiro itojun Hagino } 223686cdd19SJun-ichiro itojun Hagino } 224686cdd19SJun-ichiro itojun Hagino 225686cdd19SJun-ichiro itojun Hagino if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { 226686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[15] == 1) /* loopback */ 227686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_NODELOCAL; 228686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[15] == 0) /* unspecified */ 229686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 230686cdd19SJun-ichiro itojun Hagino } 231686cdd19SJun-ichiro itojun Hagino 232686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; 233686cdd19SJun-ichiro itojun Hagino } 234686cdd19SJun-ichiro itojun Hagino 235686cdd19SJun-ichiro itojun Hagino int 236686cdd19SJun-ichiro itojun Hagino in6_addr2scopeid(ifp, addr) 237686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; /* must not be NULL */ 238686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; /* must not be NULL */ 239686cdd19SJun-ichiro itojun Hagino { 240686cdd19SJun-ichiro itojun Hagino int scope = in6_addrscope(addr); 241686cdd19SJun-ichiro itojun Hagino 242686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 243686cdd19SJun-ichiro itojun Hagino return(0); /* XXX */ 244686cdd19SJun-ichiro itojun Hagino if (ifp->if_index >= if_indexlim) 245686cdd19SJun-ichiro itojun Hagino return(0); /* XXX */ 246686cdd19SJun-ichiro itojun Hagino 247686cdd19SJun-ichiro itojun Hagino #define SID scope6_ids[ifp->if_index] 248686cdd19SJun-ichiro itojun Hagino switch(scope) { 249686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_NODELOCAL: 250686cdd19SJun-ichiro itojun Hagino return(-1); /* XXX: is this an appropriate value? */ 251686cdd19SJun-ichiro itojun Hagino 252686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_LINKLOCAL: 253686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 254686cdd19SJun-ichiro itojun Hagino 255686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_SITELOCAL: 256686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 257686cdd19SJun-ichiro itojun Hagino 258686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_ORGLOCAL: 259686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 260686cdd19SJun-ichiro itojun Hagino 261686cdd19SJun-ichiro itojun Hagino default: 262686cdd19SJun-ichiro itojun Hagino return(0); /* XXX: treat as global. */ 263686cdd19SJun-ichiro itojun Hagino } 264686cdd19SJun-ichiro itojun Hagino #undef SID 265686cdd19SJun-ichiro itojun Hagino } 266686cdd19SJun-ichiro itojun Hagino 267686cdd19SJun-ichiro itojun Hagino void 268686cdd19SJun-ichiro itojun Hagino scope6_setdefault(ifp) 269686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; /* note that this might be NULL */ 270686cdd19SJun-ichiro itojun Hagino { 271686cdd19SJun-ichiro itojun Hagino /* 272686cdd19SJun-ichiro itojun Hagino * Currently, this function just set the default "link" according to 273686cdd19SJun-ichiro itojun Hagino * the given interface. 274686cdd19SJun-ichiro itojun Hagino * We might eventually have to separate the notion of "link" from 275686cdd19SJun-ichiro itojun Hagino * "interface" and provide a user interface to set the default. 276686cdd19SJun-ichiro itojun Hagino */ 277686cdd19SJun-ichiro itojun Hagino if (ifp) { 278686cdd19SJun-ichiro itojun Hagino scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 279686cdd19SJun-ichiro itojun Hagino ifp->if_index; 280686cdd19SJun-ichiro itojun Hagino } 281686cdd19SJun-ichiro itojun Hagino else 282686cdd19SJun-ichiro itojun Hagino scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 283686cdd19SJun-ichiro itojun Hagino } 284686cdd19SJun-ichiro itojun Hagino 285686cdd19SJun-ichiro itojun Hagino int 286686cdd19SJun-ichiro itojun Hagino scope6_get_default(idlist) 287686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 288686cdd19SJun-ichiro itojun Hagino { 289686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 290686cdd19SJun-ichiro itojun Hagino return(EINVAL); 291686cdd19SJun-ichiro itojun Hagino 292686cdd19SJun-ichiro itojun Hagino bcopy(scope6_ids[0].s6id_list, idlist, 293686cdd19SJun-ichiro itojun Hagino sizeof(scope6_ids[0].s6id_list)); 294686cdd19SJun-ichiro itojun Hagino 295686cdd19SJun-ichiro itojun Hagino return(0); 296686cdd19SJun-ichiro itojun Hagino } 297686cdd19SJun-ichiro itojun Hagino 298686cdd19SJun-ichiro itojun Hagino u_int32_t 299686cdd19SJun-ichiro itojun Hagino scope6_addr2default(addr) 300686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; 301686cdd19SJun-ichiro itojun Hagino { 302686cdd19SJun-ichiro itojun Hagino return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 303686cdd19SJun-ichiro itojun Hagino } 304