2  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * The contents of this file constitute Original Code as defined in and 
   7  * are subject to the Apple Public Source License Version 1.1 (the 
   8  * "License").  You may not use this file except in compliance with the 
   9  * License.  Please obtain a copy of the License at 
  10  * http://www.apple.com/publicsource and read it before using this file. 
  12  * This Original Code and all software distributed under the License are 
  13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the 
  17  * License for the specific language governing rights and limitations 
  20  * @APPLE_LICENSE_HEADER_END@ 
  22 /* Copyright (c) 1997, 1998 Apple Computer, Inc. All Rights Reserved */ 
  24  *      @(#)ndrv.c      1.1 (MacOSX) 6/10/43 
  25  * Justin Walker, 970604 
  27  * 980130 - Cleanup, reorg, performance improvemements 
  28  * 000816 - Removal of Y adapter cruft 
  32  * PF_NDRV allows raw access to a specified network device, directly 
  33  *  with a socket.  Expected use involves a socket option to request 
  34  *  protocol packets.  This lets ndrv_output() call dlil_output(), and 
  35  *  lets DLIL find the proper recipient for incoming packets. 
  36  *  The purpose here is for user-mode protocol implementation. 
  37  * Note that "pure raw access" will still be accomplished with BPF. 
  39  * In addition to the former use, when combined with socket NKEs, 
  40  * PF_NDRV permits a fairly flexible mechanism for implementing 
  41  * strange protocol support.  One of the main ones will be the 
  42  * BlueBox/Classic Shared IP Address support. 
  45 #include <sys/param.h> 
  46 #include <sys/systm.h> 
  47 #include <sys/kernel.h> 
  48 #include <sys/malloc.h> 
  50 #include <sys/protosw.h> 
  51 #include <sys/domain.h> 
  52 #include <sys/socket.h> 
  53 #include <sys/socketvar.h> 
  54 #include <sys/ioctl.h> 
  55 #include <sys/errno.h> 
  56 #include <sys/syslog.h> 
  59 #include <kern/queue.h> 
  62 #include <net/netisr.h> 
  63 #include <net/route.h> 
  64 #include <net/if_llc.h> 
  65 #include <net/if_dl.h> 
  66 #include <net/if_types.h> 
  71 #include <netinet/in.h> 
  72 #include <netinet/in_var.h> 
  74 #include <netinet/if_ether.h> 
  78 #include <netns/ns_if.h> 
  82 #include <netiso/argo_debug.h> 
  83 #include <netiso/iso.h> 
  84 #include <netiso/iso_var.h> 
  85 #include <netiso/iso_snpac.h> 
  89 #include <netccitt/dll.h> 
  90 #include <netccitt/llc_var.h> 
  93 #include <machine/spl.h> 
  95 int ndrv_do_detach(struct ndrv_cb 
*); 
  96 int ndrv_do_disconnect(struct ndrv_cb 
*); 
  98 unsigned long  ndrv_sendspace 
= NDRVSNDQ
; 
  99 unsigned long  ndrv_recvspace 
= NDRVRCVQ
; 
 100 struct ndrv_cb ndrvl
;           /* Head of controlblock list */ 
 102 /* To handle input, need to map tag to ndrv_cb */ 
 104 {       unsigned int tm_tag
;            /* Tag in use */ 
 105         struct ndrv_cb 
*tm_np
;          /* Owning device */ 
 106         struct dlil_demux_desc 
*tm_dm
;  /* Our local copy */ 
 109 struct ndrv_tag_map 
*ndrv_tags
; 
 110 #define TAG_MAP_COUNT 10 
 113 struct domain ndrvdomain
; 
 114 extern struct protosw ndrvsw
[]; 
 118  * Protocol init function for NDRV protocol 
 119  * Init the control block list. 
 124         ndrvl
.nd_next 
= ndrvl
.nd_prev 
= &ndrvl
; 
 128  * Protocol output - Called to output a raw network packet directly 
 132 ndrv_output(register struct mbuf 
*m
, register struct socket 
*so
) 
 133 {       register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 134         register struct ifnet 
*ifp 
= np
->nd_if
; 
 136         extern void kprintf(const char *, ...); 
 139         kprintf("NDRV output: %x, %x, %x\n", m
, so
, np
); 
 143          * No header is a format error 
 145         if ((m
->m_flags
&M_PKTHDR
) == 0) 
 149          * Can't do multicast accounting because we don't know 
 150          *  (a) if our interface does multicast; and 
 151          *  (b) what a multicast address looks like 
 156          * Can't call DLIL to do the job - we don't have a tag 
 157          *  and we aren't really a protocol 
 160         (*ifp
->if_output
)(ifp
, m
); 
 166 ndrv_input(struct mbuf 
*m
, 
 173         struct sockaddr_dl ndrvsrc 
= {sizeof (struct sockaddr_dl
), AF_NDRV
}; 
 174         register struct ndrv_cb 
*np
; 
 175         extern struct ndrv_cb 
*ndrv_find_tag(unsigned int); 
 178         /* move packet from if queue to socket */ 
 179         /* Should be media-independent */ 
 180         ndrvsrc
.sdl_type 
= IFT_ETHER
; 
 181         ndrvsrc
.sdl_nlen 
= 0; 
 182         ndrvsrc
.sdl_alen 
= 6; 
 183         ndrvsrc
.sdl_slen 
= 0; 
 184         bcopy(frame_header
, &ndrvsrc
.sdl_data
, 6); 
 187         np 
= ndrv_find_tag(dl_tag
); 
 193         if (sbappendaddr(&(so
->so_rcv
), (struct sockaddr 
*)&ndrvsrc
, 
 194                          m
, (struct mbuf 
*)0) == 0) 
 195         {       /* yes, sbappendaddr returns zero if the sockbuff is full... */ 
 205 ndrv_ioctl(unsigned long dl_tag
, 
 207            unsigned long command
, 
 211                 return((*ifp
->if_ioctl
)(ifp
, command
, data
)); 
 215 ndrv_control(struct socket 
*so
, u_long cmd
, caddr_t data
, 
 216                   struct ifnet 
*ifp
, struct proc 
*p
) 
 222  * Allocate an ndrv control block and some buffer space for the socket 
 225 ndrv_attach(struct socket 
*so
, int proto
, struct proc 
*p
) 
 227         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 229         if ((so
->so_state 
& SS_PRIV
) == 0) 
 233         kprintf("NDRV attach: %x, %x, %x\n", so
, proto
, np
); 
 235         MALLOC(np
, struct ndrv_cb 
*, sizeof(*np
), M_PCB
, M_WAITOK
); 
 239         kprintf("NDRV attach: %x, %x, %x\n", so
, proto
, np
); 
 241         if ((so
->so_pcb 
= (caddr_t
)np
)) 
 242                 bzero(np
, sizeof(*np
)); 
 245         if ((error 
= soreserve(so
, ndrv_sendspace
, ndrv_recvspace
))) 
 247         TAILQ_INIT(&np
->nd_dlist
); 
 248         np
->nd_signature 
= NDRV_SIGNATURE
; 
 250         np
->nd_proto
.sp_family 
= so
->so_proto
->pr_domain
->dom_family
; 
 251         np
->nd_proto
.sp_protocol 
= proto
; 
 252         insque((queue_t
)np
, (queue_t
)&ndrvl
); 
 257  * Destroy state just before socket deallocation. 
 258  * Flush data or not depending on the options. 
 262 ndrv_detach(struct socket 
*so
) 
 264         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 268         return ndrv_do_detach(np
); 
 273  * If a socket isn't bound to a single address, 
 274  * the ndrv input routine will hand it anything 
 275  * within that protocol family (assuming there's 
 276  * nothing else around it should go to). 
 278  * Don't expect this to be used. 
 281 int ndrv_connect(struct socket 
*so
, struct sockaddr 
*nam
, struct proc 
*p
) 
 283         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 291         bcopy((caddr_t
) nam
, (caddr_t
) np
->nd_faddr
, sizeof(struct sockaddr_ndrv
)); 
 297  * This is the "driver open" hook - we 'bind' to the 
 299  * Here's where we latch onto the driver and make it ours. 
 302 ndrv_bind(struct socket 
*so
, struct sockaddr 
*nam
, struct proc 
*p
) 
 303 {       register struct sockaddr_ndrv 
*sa 
= (struct sockaddr_ndrv 
*) nam
; 
 304         register char *dname
; 
 305         register struct ndrv_cb 
*np
; 
 306         register struct ifnet 
*ifp
; 
 307         extern int name_cmp(struct ifnet 
*, char *); 
 309         if TAILQ_EMPTY(&ifnet
) 
 310                 return(EADDRNOTAVAIL
); /* Quick sanity check */ 
 316                 return EINVAL
;                  /* XXX */ 
 318         /* I think we just latch onto a copy here; the caller frees */ 
 319         np
->nd_laddr 
= _MALLOC(sizeof(struct sockaddr_ndrv
), M_IFADDR
, M_WAITOK
); 
 320         if (np
->nd_laddr 
== NULL
) 
 322         bcopy((caddr_t
) sa
, (caddr_t
) np
->nd_laddr
, sizeof(struct sockaddr_ndrv
)); 
 323         dname 
= sa
->snd_name
; 
 327         kprintf("NDRV bind: %x, %x, %s\n", so
, np
, dname
); 
 329         /* Track down the driver and its ifnet structure. 
 330          * There's no internal call for this so we have to dup the code 
 333         TAILQ_FOREACH(ifp
, &ifnet
, if_link
) { 
 334                 if (name_cmp(ifp
, dname
) == 0) 
 339                 return(EADDRNOTAVAIL
); 
 345 ndrv_disconnect(struct socket 
*so
) 
 347         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 352         if (np
->nd_faddr 
== 0) 
 355         ndrv_do_disconnect(np
); 
 360  * Mark the connection as being incapable of further input. 
 363 ndrv_shutdown(struct socket 
*so
) 
 370  * Ship a packet out.  The ndrv output will pass it 
 371  *  to the appropriate driver.  The really tricky part 
 372  *  is the destination address... 
 375 ndrv_send(struct socket 
*so
, int flags
, struct mbuf 
*m
, 
 376           struct sockaddr 
*addr
, struct mbuf 
*control
, 
 384         error 
= ndrv_output(m
, so
); 
 391 ndrv_abort(struct socket 
*so
) 
 393         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 398         ndrv_do_disconnect(np
); 
 403 ndrv_sense(struct socket 
*so
, struct stat 
*sb
) 
 406          * stat: don't bother with a blocksize. 
 412 ndrv_sockaddr(struct socket 
*so
, struct sockaddr 
**nam
) 
 414         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 420         if (np
->nd_laddr 
== 0) 
 423         len 
= np
->nd_laddr
->snd_len
; 
 424         bcopy((caddr_t
)np
->nd_laddr
, *nam
, 
 431 ndrv_peeraddr(struct socket 
*so
, struct sockaddr 
**nam
) 
 433         register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 439         if (np
->nd_faddr 
== 0) 
 442         len 
= np
->nd_faddr
->snd_len
; 
 443         bcopy((caddr_t
)np
->nd_faddr
, *nam
, 
 452 ndrv_ctlinput(int dummy1
, struct sockaddr 
*dummy2
, void *dummy3
) 
 459 ndrv_ctloutput(struct socket 
*so
, struct sockopt 
*sopt
) 
 460 {       register struct ndrv_cb 
*np 
= sotondrvcb(so
); 
 461         struct ndrv_descr nd
; 
 462         int count 
= 0, error 
= 0; 
 463         int ndrv_getspec(struct ndrv_cb 
*, 
 465                          struct ndrv_descr 
*); 
 466         int ndrv_setspec(struct ndrv_cb 
*, struct ndrv_descr 
*); 
 467         int ndrv_delspec(struct ndrv_cb 
*, struct ndrv_descr 
*); 
 469         if (sopt
->sopt_name 
!= NDRV_DMXSPECCNT
) 
 470                 error 
= sooptcopyin(sopt
, &nd
, sizeof nd
, sizeof nd
); 
 472         {       switch(sopt
->sopt_name
) 
 473                 {       case NDRV_DMXSPEC
: /* Get/Set(Add) spec list */ 
 474                                 if (sopt
->sopt_dir 
== SOPT_GET
) 
 475                                         error 
= ndrv_getspec(np
, sopt
, &nd
); 
 477                                         error 
= ndrv_setspec(np
, &nd
); 
 479                         case NDRV_DELDMXSPEC
: /* Delete specified specs */ 
 480                                 error 
= ndrv_delspec(np
, &nd
); 
 482                         case NDRV_DMXSPECCNT
: /* How many are in the list */ 
 483                                 count 
= np
->nd_descrcnt
; 
 484                                 error 
= sooptcopyout(sopt
, &count
, sizeof count
); 
 489         log(LOG_WARNING
, "NDRV CTLOUT: %x returns %d\n", sopt
->sopt_name
, 
 495 /* Drain the queues */ 
 501 /* Sysctl hook for NDRV */ 
 509 ndrv_do_detach(register struct ndrv_cb 
*np
) 
 510 {       register struct socket 
*so 
= np
->nd_socket
; 
 511         int ndrv_dump_descr(struct ndrv_cb 
*); 
 514         kprintf("NDRV detach: %x, %x\n", so
, np
); 
 516         if (!TAILQ_EMPTY(&np
->nd_dlist
)) 
 520         FREE((caddr_t
)np
, M_PCB
); 
 527 ndrv_do_disconnect(register struct ndrv_cb 
*np
) 
 530         kprintf("NDRV disconnect: %x\n", np
); 
 533         {       m_freem(dtom(np
->nd_faddr
)); 
 536         if (np
->nd_socket
->so_state 
& SS_NOFDREF
) 
 538         soisdisconnected(np
->nd_socket
); 
 543  * Try to compare a device name (q) with one of the funky ifnet 
 544  *  device names (ifp). 
 546 int name_cmp(register struct ifnet 
*ifp
, register char *q
) 
 550         static char *sprint_d(); 
 553         len 
= strlen(ifp
->if_name
); 
 554         strncpy(r
, ifp
->if_name
, IFNAMSIZ
); 
 556         (void)sprint_d(ifp
->if_unit
, r
, IFNAMSIZ
-(r
-buf
)); 
 558         kprintf("Comparing %s, %s\n", buf
, q
); 
 560         return(strncmp(buf
, q
, IFNAMSIZ
)); 
 563 /* Hackery - return a string version of a decimal number */ 
 565 sprint_d(n
, buf
, buflen
) 
 569 {       char dbuf
[IFNAMSIZ
]; 
 570         register char *cp 
= dbuf
+IFNAMSIZ
-1; 
 575                 *cp 
= "0123456789"[n 
% 10]; 
 577         } while (n 
!= 0 && buflen 
> 0); 
 578         strncpy(buf
, cp
, IFNAMSIZ
-buflen
); 
 583  * When closing, dump any enqueued mbufs. 
 586 ndrv_flushq(register struct ifqueue 
*q
) 
 587 {       register struct mbuf 
*m
; 
 603 ndrv_getspec(struct ndrv_cb 
*np
, 
 604              struct sockopt 
*sopt
, 
 605              struct ndrv_descr 
*nd
) 
 606 {       struct dlil_demux_desc 
*mp
, *mp1
; 
 609         /* Compute # structs to copy */ 
 610         i 
= k 
= min(np
->nd_descrcnt
, 
 611                     (nd
->nd_len 
/ sizeof (struct dlil_demux_desc
))); 
 612         mp 
= (struct dlil_demux_desc 
*)nd
->nd_buf
; 
 613         TAILQ_FOREACH(mp1
, &np
->nd_dlist
, next
) 
 616                 error 
= copyout(mp1
, mp
++, sizeof (struct dlil_demux_desc
)); 
 621         {       nd
->nd_len 
= i 
* (sizeof (struct dlil_demux_desc
)); 
 622                 error 
= sooptcopyout(sopt
, nd
, sizeof (*nd
)); 
 628  * Install a protocol descriptor, making us a protocol handler. 
 629  *  We expect the client to handle all output tasks (we get fully 
 630  *  formed frames from the client and hand them to the driver 
 631  *  directly).  The reason we register is to get those incoming 
 632  *  frames.  We do it as a protocol handler because the network layer 
 633  *  already knows how find the ones we want, so there's no need to 
 635  * Since this mechanism is mostly for user mode, most of the procedures 
 636  *  to be registered will be null. 
 637  * Note that we jam the pair (PF_XXX, native_type) into the native_type 
 638  *  field of the demux descriptor.  Yeah, it's a hack. 
 641 ndrv_setspec(struct ndrv_cb 
*np
, struct ndrv_descr 
*nd
) 
 642 {       struct dlil_demux_desc 
*mp
, *mp1
; 
 643         int i 
= 0, error 
= 0, j
; 
 646         struct dlil_proto_reg_str proto_spec
; 
 647         int ndrv_add_descr(struct ndrv_cb 
*, struct dlil_proto_reg_str 
*); 
 649         bzero((caddr_t
)&proto_spec
, sizeof (proto_spec
)); 
 650         i 
= nd
->nd_len 
/ (sizeof (struct dlil_demux_desc
)); /* # elts */ 
 651         MALLOC(native_values
,int *, i 
* sizeof (int), M_TEMP
, M_WAITOK
); 
 652         if (native_values 
== NULL
) 
 654         mp 
= (struct dlil_demux_desc 
*)nd
->nd_buf
; 
 655         for (j 
= 0; j
++ < i
;) 
 656         {       MALLOC(mp1
, struct dlil_demux_desc 
*, 
 657                        sizeof (struct dlil_demux_desc
), M_PCB
, M_WAITOK
); 
 662                 error 
= copyin(mp
++, mp1
, sizeof (struct dlil_demux_desc
)); 
 665                 TAILQ_INSERT_TAIL(&np
->nd_dlist
, mp1
, next
); 
 666                 value 
= (unsigned long)mp1
->native_type
; 
 667                 native_values
[j
] = (unsigned short)value
; 
 668                 mp1
->native_type 
= (char *)&native_values
[j
]; 
 669                 proto_spec
.protocol_family  
= (unsigned char)(value
>>16); /* Oy! */ 
 670                 proto_spec
.interface_family 
= np
->nd_if
->if_family
; 
 671                 proto_spec
.unit_number      
= np
->nd_if
->if_unit
; 
 673                 proto_spec
.input            
= ndrv_input
; 
 674                 proto_spec
.pre_output       
= NULL
; 
 675                 /* No event/offer functionality needed */ 
 676                 proto_spec
.event            
= NULL
; 
 677                 proto_spec
.offer            
= NULL
; 
 678                 proto_spec
.ioctl            
= ndrv_ioctl
; /* ??? */ 
 679                 /* What exactly does this do again? */ 
 680                 proto_spec
.default_proto    
= 0; 
 685         {       struct dlil_demux_desc 
*mp2
; 
 687                 while ((mp2 
= TAILQ_FIRST(&np
->nd_dlist
))) { 
 688                         TAILQ_REMOVE(&np
->nd_dlist
, mp2
, next
); 
 692                 error 
= ndrv_add_descr(np
, &proto_spec
); 
 694         log(LOG_WARNING
, "NDRV ADDSPEC: got error %d\n", error
); 
 696         FREE(native_values
, M_TEMP
); 
 701 ndrv_delspec(struct ndrv_cb 
*np
, struct ndrv_descr 
*nd
) 
 702 {       struct dlil_demux_desc 
*mp
; 
 708 ndrv_find_tag(unsigned int tag
) 
 709 {       struct ndrv_tag_map 
*tmp
; 
 713         for (i
=0; i
++ < tag_map_count
; tmp
++) 
 714                 if (tmp
->tm_tag 
== tag
) 
 720 ndrv_add_tag(struct ndrv_cb 
*np
, unsigned int tag
, 
 721              struct dlil_demux_desc 
*mp
) 
 722 {       struct ndrv_tag_map 
*tmp
; 
 726         for (i
=0; i
++ < tag_map_count
; tmp
++) 
 727                 if (tmp
->tm_tag 
== 0) 
 731                         log(LOG_WARNING
, "NDRV ADDING TAG %d\n", tag
); 
 736         /* Oops - ran out of space.  Realloc */ 
 737         i 
= tag_map_count 
+ TAG_MAP_COUNT
; 
 738         MALLOC(tmp
, struct ndrv_tag_map 
*, i 
* sizeof (struct ndrv_tag_map
), 
 742         /* Clear tail of new table, except for the slot we are creating ... */ 
 743         bzero((caddr_t
)&tmp
[tag_map_count
+1], 
 744               (TAG_MAP_COUNT
-1) * sizeof (struct ndrv_tag_map
)); 
 745         /* ...and then copy in the original piece */ 
 747                 bcopy(ndrv_tags
, tmp
, 
 748                       tag_map_count 
* sizeof (struct ndrv_tag_map
)); 
 749         /* ...and then install the new tag... */ 
 750         tmp
[tag_map_count
].tm_tag 
= tag
; 
 751         tmp
[tag_map_count
].tm_np 
= np
; 
 754                 FREE(ndrv_tags
, M_PCB
); 
 757         log(LOG_WARNING
, "NDRV ADDING TAG %d (new chunk)\n", tag
); 
 763  * Attach the proto spec list, and record the tags. 
 766 ndrv_add_descr(struct ndrv_cb 
*np
, struct dlil_proto_reg_str 
*proto_spec
) 
 767 {       unsigned long dl_tag
; 
 769         struct dlil_demux_desc 
*mp
; 
 771         /* Attach to our device to get requested packets */ 
 772         TAILQ_INIT(&proto_spec
->demux_desc_head
); 
 773         error 
= dlil_attach_protocol(proto_spec
, &dl_tag
); 
 776                 error 
= ndrv_add_tag(np
, dl_tag
, mp
); 
 782 ndrv_dump_descr(struct ndrv_cb 
*np
) 
 783 {       struct dlil_demux_desc 
*dm1
, *dm2
; 
 784         struct ndrv_tag_map 
*tmp
; 
 787         if (dm1 
= TAILQ_FIRST(&np
->nd_dlist
)) 
 788         {       for (i 
= 0, tmp 
= &ndrv_tags
[0]; i
++ < tag_map_count
; tmp
++) 
 789                         if (tmp
->tm_np 
== np
) 
 790                         {       error 
= dlil_detach_protocol(tmp
->tm_tag
); 
 792                                 {       dm2 
= TAILQ_NEXT(dm1
, next
); 
 797                                     "Detached tag %d (error %d)\n", 
 808         static int ndrv_dominited 
= 0; 
 810         if (ndrv_dominited 
== 0) { 
 811                 net_add_proto(&ndrvsw
[0], &ndrvdomain
); 
 817 struct pr_usrreqs ndrv_usrreqs 
= { 
 818         ndrv_abort
, pru_accept_notsupp
, ndrv_attach
, ndrv_bind
, 
 819         ndrv_connect
, pru_connect2_notsupp
, ndrv_control
, ndrv_detach
, 
 820         ndrv_disconnect
, pru_listen_notsupp
, ndrv_peeraddr
, pru_rcvd_notsupp
, 
 821         pru_rcvoob_notsupp
, ndrv_send
, ndrv_sense
, ndrv_shutdown
, 
 822         ndrv_sockaddr
, sosend
, soreceive
, sopoll
 
 825 struct protosw ndrvsw
[] = 
 826 {       {       SOCK_RAW
, &ndrvdomain
, 0, PR_ATOMIC
|PR_ADDR
, 
 827                 0, ndrv_output
, ndrv_ctlinput
, ndrv_ctloutput
, 
 829                 ndrv_drain
, ndrv_sysctl
, &ndrv_usrreqs
 
 833 struct domain ndrvdomain 
= 
 834 {       AF_NDRV
, "NetDriver", ndrv_dominit
, NULL
, NULL
, 
 836         NULL
, NULL
, 0, 0, 0, 0