1686cdd19SJun-ichiro itojun Hagino /* $FreeBSD$ */ 2686cdd19SJun-ichiro itojun Hagino /* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei 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> 38686cdd19SJun-ichiro itojun Hagino 39686cdd19SJun-ichiro itojun Hagino #include <net/route.h> 40686cdd19SJun-ichiro itojun Hagino #include <net/if.h> 41686cdd19SJun-ichiro itojun Hagino 42686cdd19SJun-ichiro itojun Hagino #include <netinet/in.h> 43686cdd19SJun-ichiro itojun Hagino 44686cdd19SJun-ichiro itojun Hagino #include <netinet6/in6_var.h> 45686cdd19SJun-ichiro itojun Hagino #include <netinet6/scope6_var.h> 46686cdd19SJun-ichiro itojun Hagino 47686cdd19SJun-ichiro itojun Hagino struct scope6_id { 48686cdd19SJun-ichiro itojun Hagino /* 49686cdd19SJun-ichiro itojun Hagino * 16 is correspondent to 4bit multicast scope field. 50686cdd19SJun-ichiro itojun Hagino * i.e. from node-local to global with some reserved/unassigned types. 51686cdd19SJun-ichiro itojun Hagino */ 52686cdd19SJun-ichiro itojun Hagino u_int32_t s6id_list[16]; 53686cdd19SJun-ichiro itojun Hagino }; 54686cdd19SJun-ichiro itojun Hagino static size_t if_indexlim = 8; 55686cdd19SJun-ichiro itojun Hagino struct scope6_id *scope6_ids = NULL; 56686cdd19SJun-ichiro itojun Hagino 57686cdd19SJun-ichiro itojun Hagino void 58686cdd19SJun-ichiro itojun Hagino scope6_ifattach(ifp) 59686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 60686cdd19SJun-ichiro itojun Hagino { 61686cdd19SJun-ichiro itojun Hagino int s = splnet(); 62686cdd19SJun-ichiro itojun Hagino 63686cdd19SJun-ichiro itojun Hagino /* 64686cdd19SJun-ichiro itojun Hagino * We have some arrays that should be indexed by if_index. 65686cdd19SJun-ichiro itojun Hagino * since if_index will grow dynamically, they should grow too. 66686cdd19SJun-ichiro itojun Hagino */ 67686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL || if_index >= if_indexlim) { 68686cdd19SJun-ichiro itojun Hagino size_t n; 69686cdd19SJun-ichiro itojun Hagino caddr_t q; 70686cdd19SJun-ichiro itojun Hagino 71686cdd19SJun-ichiro itojun Hagino while (if_index >= if_indexlim) 72686cdd19SJun-ichiro itojun Hagino if_indexlim <<= 1; 73686cdd19SJun-ichiro itojun Hagino 74686cdd19SJun-ichiro itojun Hagino /* grow scope index array */ 75686cdd19SJun-ichiro itojun Hagino n = if_indexlim * sizeof(struct scope6_id); 76686cdd19SJun-ichiro itojun Hagino /* XXX: need new malloc type? */ 77686cdd19SJun-ichiro itojun Hagino q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 78686cdd19SJun-ichiro itojun Hagino bzero(q, n); 79686cdd19SJun-ichiro itojun Hagino if (scope6_ids) { 80686cdd19SJun-ichiro itojun Hagino bcopy((caddr_t)scope6_ids, q, n/2); 81686cdd19SJun-ichiro itojun Hagino free((caddr_t)scope6_ids, M_IFADDR); 82686cdd19SJun-ichiro itojun Hagino } 83686cdd19SJun-ichiro itojun Hagino scope6_ids = (struct scope6_id *)q; 84686cdd19SJun-ichiro itojun Hagino } 85686cdd19SJun-ichiro itojun Hagino 86686cdd19SJun-ichiro itojun Hagino #define SID scope6_ids[ifp->if_index] 87686cdd19SJun-ichiro itojun Hagino 88686cdd19SJun-ichiro itojun Hagino /* don't initialize if called twice */ 89686cdd19SJun-ichiro itojun Hagino if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { 90686cdd19SJun-ichiro itojun Hagino splx(s); 91686cdd19SJun-ichiro itojun Hagino return; 92686cdd19SJun-ichiro itojun Hagino } 93686cdd19SJun-ichiro itojun Hagino 94686cdd19SJun-ichiro itojun Hagino /* 95686cdd19SJun-ichiro itojun Hagino * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 96686cdd19SJun-ichiro itojun Hagino * Should we rather hardcode here? 97686cdd19SJun-ichiro itojun Hagino */ 98686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 99686cdd19SJun-ichiro itojun Hagino #ifdef MULTI_SCOPE 100686cdd19SJun-ichiro itojun Hagino /* by default, we don't care about scope boundary for these scopes. */ 101686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 102686cdd19SJun-ichiro itojun Hagino SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 103686cdd19SJun-ichiro itojun Hagino #endif 104686cdd19SJun-ichiro itojun Hagino #undef SID 105686cdd19SJun-ichiro itojun Hagino 106686cdd19SJun-ichiro itojun Hagino splx(s); 107686cdd19SJun-ichiro itojun Hagino } 108686cdd19SJun-ichiro itojun Hagino 109686cdd19SJun-ichiro itojun Hagino int 110686cdd19SJun-ichiro itojun Hagino scope6_set(ifp, idlist) 111686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 112686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 113686cdd19SJun-ichiro itojun Hagino { 114686cdd19SJun-ichiro itojun Hagino int i, s; 115686cdd19SJun-ichiro itojun Hagino int error = 0; 116686cdd19SJun-ichiro itojun Hagino 117686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 118686cdd19SJun-ichiro itojun Hagino return(EINVAL); 119686cdd19SJun-ichiro itojun Hagino 120686cdd19SJun-ichiro itojun Hagino /* 121686cdd19SJun-ichiro itojun Hagino * XXX: We need more consistency checks of the relationship among 122686cdd19SJun-ichiro itojun Hagino * scopes (e.g. an organization should be larger than a site). 123686cdd19SJun-ichiro itojun Hagino */ 124686cdd19SJun-ichiro itojun Hagino 125686cdd19SJun-ichiro itojun Hagino /* 126686cdd19SJun-ichiro itojun Hagino * TODO(XXX): after setting, we should reflect the changes to 127686cdd19SJun-ichiro itojun Hagino * interface addresses, routing table entries, PCB entries... 128686cdd19SJun-ichiro itojun Hagino */ 129686cdd19SJun-ichiro itojun Hagino 130686cdd19SJun-ichiro itojun Hagino s = splnet(); 131686cdd19SJun-ichiro itojun Hagino 132686cdd19SJun-ichiro itojun Hagino for (i = 0; i < 16; i++) { 133686cdd19SJun-ichiro itojun Hagino if (idlist[i] && 134686cdd19SJun-ichiro itojun Hagino idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { 135686cdd19SJun-ichiro itojun Hagino if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 136686cdd19SJun-ichiro itojun Hagino idlist[i] > if_index) { 137686cdd19SJun-ichiro itojun Hagino /* 138686cdd19SJun-ichiro itojun Hagino * XXX: theoretically, there should be no 139686cdd19SJun-ichiro itojun Hagino * relationship between link IDs and interface 140686cdd19SJun-ichiro itojun Hagino * IDs, but we check the consistency for 141686cdd19SJun-ichiro itojun Hagino * safety in later use. 142686cdd19SJun-ichiro itojun Hagino */ 143686cdd19SJun-ichiro itojun Hagino splx(s); 144686cdd19SJun-ichiro itojun Hagino return(EINVAL); 145686cdd19SJun-ichiro itojun Hagino } 146686cdd19SJun-ichiro itojun Hagino 147686cdd19SJun-ichiro itojun Hagino /* 148686cdd19SJun-ichiro itojun Hagino * XXX: we must need lots of work in this case, 149686cdd19SJun-ichiro itojun Hagino * but we simply set the new value in this initial 150686cdd19SJun-ichiro itojun Hagino * implementation. 151686cdd19SJun-ichiro itojun Hagino */ 152686cdd19SJun-ichiro itojun Hagino scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; 153686cdd19SJun-ichiro itojun Hagino } 154686cdd19SJun-ichiro itojun Hagino } 155686cdd19SJun-ichiro itojun Hagino splx(s); 156686cdd19SJun-ichiro itojun Hagino 157686cdd19SJun-ichiro itojun Hagino return(error); 158686cdd19SJun-ichiro itojun Hagino } 159686cdd19SJun-ichiro itojun Hagino 160686cdd19SJun-ichiro itojun Hagino int 161686cdd19SJun-ichiro itojun Hagino scope6_get(ifp, idlist) 162686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; 163686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 164686cdd19SJun-ichiro itojun Hagino { 165686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 166686cdd19SJun-ichiro itojun Hagino return(EINVAL); 167686cdd19SJun-ichiro itojun Hagino 168686cdd19SJun-ichiro itojun Hagino bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, 169686cdd19SJun-ichiro itojun Hagino sizeof(scope6_ids[ifp->if_index].s6id_list)); 170686cdd19SJun-ichiro itojun Hagino 171686cdd19SJun-ichiro itojun Hagino return(0); 172686cdd19SJun-ichiro itojun Hagino } 173686cdd19SJun-ichiro itojun Hagino 174686cdd19SJun-ichiro itojun Hagino 175686cdd19SJun-ichiro itojun Hagino /* 176686cdd19SJun-ichiro itojun Hagino * Get a scope of the address. Node-local, link-local, site-local or global. 177686cdd19SJun-ichiro itojun Hagino */ 178686cdd19SJun-ichiro itojun Hagino int 179686cdd19SJun-ichiro itojun Hagino in6_addrscope(addr) 180686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; 181686cdd19SJun-ichiro itojun Hagino { 182686cdd19SJun-ichiro itojun Hagino int scope; 183686cdd19SJun-ichiro itojun Hagino 184686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[0] == 0xfe) { 185686cdd19SJun-ichiro itojun Hagino scope = addr->s6_addr8[1] & 0xc0; 186686cdd19SJun-ichiro itojun Hagino 187686cdd19SJun-ichiro itojun Hagino switch (scope) { 188686cdd19SJun-ichiro itojun Hagino case 0x80: 189686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 190686cdd19SJun-ichiro itojun Hagino break; 191686cdd19SJun-ichiro itojun Hagino case 0xc0: 192686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_SITELOCAL; 193686cdd19SJun-ichiro itojun Hagino break; 194686cdd19SJun-ichiro itojun Hagino default: 195686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 196686cdd19SJun-ichiro itojun Hagino break; 197686cdd19SJun-ichiro itojun Hagino } 198686cdd19SJun-ichiro itojun Hagino } 199686cdd19SJun-ichiro itojun Hagino 200686cdd19SJun-ichiro itojun Hagino 201686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[0] == 0xff) { 202686cdd19SJun-ichiro itojun Hagino scope = addr->s6_addr8[1] & 0x0f; 203686cdd19SJun-ichiro itojun Hagino 204686cdd19SJun-ichiro itojun Hagino /* 205686cdd19SJun-ichiro itojun Hagino * due to other scope such as reserved, 206686cdd19SJun-ichiro itojun Hagino * return scope doesn't work. 207686cdd19SJun-ichiro itojun Hagino */ 208686cdd19SJun-ichiro itojun Hagino switch (scope) { 209686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_NODELOCAL: 210686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_NODELOCAL; 211686cdd19SJun-ichiro itojun Hagino break; 212686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_LINKLOCAL: 213686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 214686cdd19SJun-ichiro itojun Hagino break; 215686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_SITELOCAL: 216686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_SITELOCAL; 217686cdd19SJun-ichiro itojun Hagino break; 218686cdd19SJun-ichiro itojun Hagino default: 219686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; 220686cdd19SJun-ichiro itojun Hagino break; 221686cdd19SJun-ichiro itojun Hagino } 222686cdd19SJun-ichiro itojun Hagino } 223686cdd19SJun-ichiro itojun Hagino 224686cdd19SJun-ichiro itojun Hagino if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { 225686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[15] == 1) /* loopback */ 226686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_NODELOCAL; 227686cdd19SJun-ichiro itojun Hagino if (addr->s6_addr8[15] == 0) /* unspecified */ 228686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_LINKLOCAL; 229686cdd19SJun-ichiro itojun Hagino } 230686cdd19SJun-ichiro itojun Hagino 231686cdd19SJun-ichiro itojun Hagino return IPV6_ADDR_SCOPE_GLOBAL; 232686cdd19SJun-ichiro itojun Hagino } 233686cdd19SJun-ichiro itojun Hagino 234686cdd19SJun-ichiro itojun Hagino int 235686cdd19SJun-ichiro itojun Hagino in6_addr2scopeid(ifp, addr) 236686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; /* must not be NULL */ 237686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; /* must not be NULL */ 238686cdd19SJun-ichiro itojun Hagino { 239686cdd19SJun-ichiro itojun Hagino int scope = in6_addrscope(addr); 240686cdd19SJun-ichiro itojun Hagino 241686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 242686cdd19SJun-ichiro itojun Hagino return(0); /* XXX */ 243686cdd19SJun-ichiro itojun Hagino if (ifp->if_index >= if_indexlim) 244686cdd19SJun-ichiro itojun Hagino return(0); /* XXX */ 245686cdd19SJun-ichiro itojun Hagino 246686cdd19SJun-ichiro itojun Hagino #define SID scope6_ids[ifp->if_index] 247686cdd19SJun-ichiro itojun Hagino switch(scope) { 248686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_NODELOCAL: 249686cdd19SJun-ichiro itojun Hagino return(-1); /* XXX: is this an appropriate value? */ 250686cdd19SJun-ichiro itojun Hagino 251686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_LINKLOCAL: 252686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); 253686cdd19SJun-ichiro itojun Hagino 254686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_SITELOCAL: 255686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); 256686cdd19SJun-ichiro itojun Hagino 257686cdd19SJun-ichiro itojun Hagino case IPV6_ADDR_SCOPE_ORGLOCAL: 258686cdd19SJun-ichiro itojun Hagino return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); 259686cdd19SJun-ichiro itojun Hagino 260686cdd19SJun-ichiro itojun Hagino default: 261686cdd19SJun-ichiro itojun Hagino return(0); /* XXX: treat as global. */ 262686cdd19SJun-ichiro itojun Hagino } 263686cdd19SJun-ichiro itojun Hagino #undef SID 264686cdd19SJun-ichiro itojun Hagino } 265686cdd19SJun-ichiro itojun Hagino 266686cdd19SJun-ichiro itojun Hagino void 267686cdd19SJun-ichiro itojun Hagino scope6_setdefault(ifp) 268686cdd19SJun-ichiro itojun Hagino struct ifnet *ifp; /* note that this might be NULL */ 269686cdd19SJun-ichiro itojun Hagino { 270686cdd19SJun-ichiro itojun Hagino /* 271686cdd19SJun-ichiro itojun Hagino * Currently, this function just set the default "link" according to 272686cdd19SJun-ichiro itojun Hagino * the given interface. 273686cdd19SJun-ichiro itojun Hagino * We might eventually have to separate the notion of "link" from 274686cdd19SJun-ichiro itojun Hagino * "interface" and provide a user interface to set the default. 275686cdd19SJun-ichiro itojun Hagino */ 276686cdd19SJun-ichiro itojun Hagino if (ifp) { 277686cdd19SJun-ichiro itojun Hagino scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 278686cdd19SJun-ichiro itojun Hagino ifp->if_index; 279686cdd19SJun-ichiro itojun Hagino } 280686cdd19SJun-ichiro itojun Hagino else 281686cdd19SJun-ichiro itojun Hagino scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 282686cdd19SJun-ichiro itojun Hagino } 283686cdd19SJun-ichiro itojun Hagino 284686cdd19SJun-ichiro itojun Hagino int 285686cdd19SJun-ichiro itojun Hagino scope6_get_default(idlist) 286686cdd19SJun-ichiro itojun Hagino u_int32_t *idlist; 287686cdd19SJun-ichiro itojun Hagino { 288686cdd19SJun-ichiro itojun Hagino if (scope6_ids == NULL) /* paranoid? */ 289686cdd19SJun-ichiro itojun Hagino return(EINVAL); 290686cdd19SJun-ichiro itojun Hagino 291686cdd19SJun-ichiro itojun Hagino bcopy(scope6_ids[0].s6id_list, idlist, 292686cdd19SJun-ichiro itojun Hagino sizeof(scope6_ids[0].s6id_list)); 293686cdd19SJun-ichiro itojun Hagino 294686cdd19SJun-ichiro itojun Hagino return(0); 295686cdd19SJun-ichiro itojun Hagino } 296686cdd19SJun-ichiro itojun Hagino 297686cdd19SJun-ichiro itojun Hagino u_int32_t 298686cdd19SJun-ichiro itojun Hagino scope6_addr2default(addr) 299686cdd19SJun-ichiro itojun Hagino struct in6_addr *addr; 300686cdd19SJun-ichiro itojun Hagino { 301686cdd19SJun-ichiro itojun Hagino return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); 302686cdd19SJun-ichiro itojun Hagino } 303