OpenVAS Scanner  7.0.1~git
pcap_openvas.h File Reference

Header file for module pcap. More...

#include <arpa/inet.h>
#include <pcap.h>
#include <sys/param.h>
Include dependency graph for pcap_openvas.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

int v6_is_local_ip (struct in6_addr *)
 
int islocalhost (struct in_addr *)
 Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface. More...
 
int v6_islocalhost (struct in6_addr *)
 Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface. More...
 
int get_datalink_size (int)
 
char * routethrough (struct in_addr *, struct in_addr *)
 An awesome function to determine what interface a packet to a given destination should be routed through. More...
 
char * v6_routethrough (struct in6_addr *, struct in6_addr *)
 An awesome function to determine what interface a packet to a given destination should be routed through. More...
 
int v6_getsourceip (struct in6_addr *, struct in6_addr *)
 

Detailed Description

Header file for module pcap.

Definition in file pcap_openvas.h.

Function Documentation

◆ get_datalink_size()

int get_datalink_size ( int  )

Definition at line 295 of file pcap.c.

Referenced by capture_next_packet(), capture_next_v6_packet(), nasl_pcap_next(), nasl_send_capture(), and scan().

296 {
297  int offset = -1;
298  switch (datalink)
299  {
300  case DLT_EN10MB:
301  offset = 14;
302  break;
303  case DLT_IEEE802:
304  offset = 22;
305  break;
306  case DLT_NULL:
307  offset = 4;
308  break;
309  case DLT_SLIP:
310 #if (FREEBSD || OPENBSD || NETBSD || BSDI || DARWIN)
311  offset = 16;
312 #else
313  offset = 24; /* Anyone use this??? */
314 #endif
315  break;
316  case DLT_PPP:
317 #if (FREEBSD || OPENBSD || NETBSD || BSDI || DARWIN)
318  offset = 4;
319 #else
320 #ifdef SOLARIS
321  offset = 8;
322 #else
323  offset = 24; /* Anyone use this? */
324 #endif /* ifdef solaris */
325 #endif /* if freebsd || openbsd || netbsd || bsdi */
326  break;
327  case DLT_RAW:
328  offset = 0;
329  break;
330  }
331  return offset;
332 }
Here is the caller graph for this function:

◆ islocalhost()

int islocalhost ( struct in_addr *  )

Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface.

Definition at line 268 of file pcap.c.

269 {
270  char dev[128];
271 
272  if (addr == NULL)
273  return -1;
274 
275  /* If it is 0.0.0.0 or starts with 127.0.0.1 then it is
276  probably localhost */
277  if ((addr->s_addr & htonl (0xFF000000)) == htonl (0x7F000000))
278  return 1;
279 
280  if (!addr->s_addr)
281  return 1;
282 
283  /* If it is the same addy as a local interface, then it is
284  probably localhost */
285 
286  if (ipaddr2devname (dev, sizeof (dev), addr) != -1)
287  return 1;
288 
289  /* OK, so to a first approximation, this addy is probably not
290  localhost */
291  return 0;
292 }
int ipaddr2devname(char *dev, int sz, struct in_addr *addr)
Definition: pcap.c:204

◆ routethrough()

char* routethrough ( struct in_addr *  dest,
struct in_addr *  source 
)

An awesome function to determine what interface a packet to a given destination should be routed through.

It returns NULL if no appropriate interface is found, otherwise it returns the device name and fills in the source parameter. Some of the stuff is from Stevens' Unix Network Programming V2. He had an easier suggestion for doing this (in the book), but it isn't portable :(

Definition at line 975 of file pcap.c.

References myroute::dest, myroute::dev, getinterfaces(), islocalhost(), myroute::mask, MAXROUTES, interface_info::name, and name.

Referenced by init_capture_device(), nasl_pcap_next(), nasl_send_capture(), nasl_tcp_ping(), and openbpf().

976 {
977  static int initialized = 0;
978  int i;
979  char buf[10240];
980  struct interface_info *mydevs;
981  static struct myroute
982  {
983  struct interface_info *dev;
984  unsigned long mask;
985  unsigned long dest;
986  } myroutes[MAXROUTES];
987  int numinterfaces = 0;
988  char *p, *endptr;
989  char iface[64];
990  static int numroutes = 0;
991  FILE *routez;
992  long match = -1;
993  unsigned long bestmatch = 0;
994 
995  struct in_addr src;
996 
997  gvm_source_addr (&src);
998  if (!dest)
999  {
1000  g_message ("ipaddr2devname passed a NULL dest address");
1001  return NULL;
1002  }
1003 
1004  if (!initialized)
1005  {
1006  /* Dummy socket for ioctl */
1007  initialized = 1;
1008  mydevs = getinterfaces (&numinterfaces);
1009  if (!mydevs)
1010  return NULL;
1011 
1012  routez = fopen ("/proc/net/route", "r");
1013  if (routez)
1014  {
1015  /* OK, linux style /proc/net/route ... we can handle this ... */
1016  /* Now that we've got the interfaces, we g0 after the r0ut3Z */
1017  if (fgets (buf, sizeof (buf), routez)
1018  == NULL) /* Kill the first line */
1019  g_message ("Could not read from /proc/net/route");
1020  while (fgets (buf, sizeof (buf), routez))
1021  {
1022  p = strtok (buf, " \t\n");
1023  if (!p)
1024  {
1025  g_message ("Could not find interface in"
1026  " /proc/net/route line");
1027  continue;
1028  }
1029  strncpy (iface, p, sizeof (iface));
1030  if ((p = strchr (iface, ':')))
1031  {
1032  *p = '\0'; /* To support IP aliasing */
1033  }
1034  p = strtok (NULL, " \t\n");
1035  endptr = NULL;
1036  myroutes[numroutes].dest = strtoul (p, &endptr, 16);
1037  if (!endptr || *endptr)
1038  {
1039  g_message (
1040  "Failed to determine Destination from /proc/net/route");
1041  continue;
1042  }
1043  for (i = 0; i < 6; i++)
1044  {
1045  p = strtok (NULL, " \t\n");
1046  if (!p)
1047  break;
1048  }
1049  if (!p)
1050  {
1051  g_message ("Failed to find field %d in"
1052  " /proc/net/route",
1053  i + 2);
1054  continue;
1055  }
1056  endptr = NULL;
1057  myroutes[numroutes].mask = strtoul (p, &endptr, 16);
1058  if (!endptr || *endptr)
1059  {
1060  g_message ("Failed to determine mask"
1061  " from /proc/net/route");
1062  continue;
1063  }
1064 
1065  g_debug ("#%d: for dev %s, The dest is %lX and the mask is %lX\n",
1066  numroutes, iface, myroutes[numroutes].dest,
1067  myroutes[numroutes].mask);
1068  for (i = 0; i < numinterfaces; i++)
1069  if (!strcmp (iface, mydevs[i].name))
1070  {
1071  myroutes[numroutes].dev = &mydevs[i];
1072  break;
1073  }
1074  if (i == numinterfaces)
1075  g_message (
1076  "Failed to find interface %s mentioned in /proc/net/route",
1077  iface);
1078  numroutes++;
1079  if (numroutes >= MAXROUTES)
1080  {
1081  g_message ("You seem to have WAY to many routes!");
1082  break;
1083  }
1084  }
1085  fclose (routez);
1086  }
1087  else
1088  {
1089  g_message ("Could not read from /proc/net/route");
1090  return NULL;
1091  }
1092  }
1093  else
1094  mydevs = getinterfaces (&numinterfaces);
1095  /* WHEW, that takes care of initializing, now we have the easy job of
1096  finding which route matches */
1097  if (mydevs && islocalhost (dest))
1098  {
1099  if (source)
1100  source->s_addr = htonl (0x7F000001);
1101  /* Now we find the localhost interface name, assuming 127.0.0.1 is
1102  localhost (it damn well better be!)... */
1103  for (i = 0; i < numinterfaces; i++)
1104  {
1105  if (mydevs[i].addr.s_addr == htonl (0x7F000001))
1106  {
1107  return mydevs[i].name;
1108  }
1109  }
1110  return NULL;
1111  }
1112 
1113  for (i = 0; i < numroutes; i++)
1114  {
1115  if ((dest->s_addr & myroutes[i].mask) == myroutes[i].dest
1116  && myroutes[i].mask >= bestmatch)
1117  {
1118  if (source)
1119  {
1120  if (src.s_addr != INADDR_ANY)
1121  source->s_addr = src.s_addr;
1122  else
1123  source->s_addr = myroutes[i].dev->addr.s_addr;
1124  }
1125  match = i;
1126  bestmatch = myroutes[i].mask;
1127  }
1128  }
1129  if (match != -1)
1130  return myroutes[match].dev->name;
1131  return NULL;
1132 }
Definition: pcap.c:58
struct interface_info * dev
Definition: pcap.c:60
int islocalhost(struct in_addr *addr)
Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface...
Definition: pcap.c:268
struct interface_info * getinterfaces(int *howmany)
Definition: pcap.c:410
const char * name
Definition: nasl_init.c:377
unsigned long dest
Definition: pcap.c:63
unsigned long mask
Definition: pcap.c:62
char name[64]
Definition: pcap.c:52
#define MAXROUTES
Definition: pcap.c:42
Here is the call graph for this function:
Here is the caller graph for this function:

◆ v6_getsourceip()

int v6_getsourceip ( struct in6_addr *  ,
struct in6_addr *   
)

Definition at line 478 of file pcap.c.

References name.

Referenced by nasl_this_host(), and v6_routethrough().

479 {
480  int sd;
481  struct sockaddr_in sock;
482  unsigned int socklen;
483  unsigned short p1;
484 
485  p1 = (unsigned short) rand ();
486  if (p1 < 5000)
487  p1 += 5000;
488 
489  if (IN6_IS_ADDR_V4MAPPED (dst))
490  {
491  char name[INET6_ADDRSTRLEN];
492 
493  if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
494  {
495  perror ("Socket troubles");
496  return 0;
497  }
498  bzero (&sock, sizeof (struct sockaddr_in));
499  sock.sin_family = AF_INET;
500  sock.sin_addr.s_addr = dst->s6_addr32[3];
501  sock.sin_port = htons (p1);
502  if (connect (sd, (struct sockaddr *) &sock, sizeof (struct sockaddr_in))
503  == -1)
504  {
505  close (sd);
506  return 0;
507  }
508  bzero (&sock, sizeof (struct sockaddr_in));
509  socklen = sizeof (struct sockaddr_in);
510  if (getsockname (sd, (struct sockaddr *) &sock, &socklen) == -1)
511  {
512  perror ("getsockname");
513  close (sd);
514  return 0;
515  }
516 
517  src->s6_addr32[0] = 0;
518  src->s6_addr32[1] = 0;
519  src->s6_addr32[2] = htonl (0xffff);
520  src->s6_addr32[3] = sock.sin_addr.s_addr;
521  g_debug ("source address is %s\n",
522  inet_ntop (AF_INET6, src, name, sizeof (name)));
523  close (sd);
524  }
525  else
526  {
527  struct sockaddr_in6 sock6;
528  char name[INET6_ADDRSTRLEN];
529 
530  if ((sd = socket (AF_INET6, SOCK_DGRAM, 0)) == -1)
531  {
532  perror ("Socket troubles");
533  return 0;
534  }
535  bzero (&sock6, sizeof (sock6));
536  sock6.sin6_family = AF_INET6;
537  sock6.sin6_addr.s6_addr32[0] = dst->s6_addr32[0];
538  sock6.sin6_addr.s6_addr32[1] = dst->s6_addr32[1];
539  sock6.sin6_addr.s6_addr32[2] = dst->s6_addr32[2];
540  sock6.sin6_addr.s6_addr32[3] = dst->s6_addr32[3];
541  sock6.sin6_port = htons (p1);
542  if (connect (sd, (struct sockaddr *) &sock6, sizeof (struct sockaddr_in6))
543  == -1)
544  {
545  close (sd);
546  return 0;
547  }
548  bzero (&sock6, sizeof (struct sockaddr_in6));
549  socklen = sizeof (struct sockaddr_in6);
550  if (getsockname (sd, (struct sockaddr *) &sock6, &socklen) == -1)
551  {
552  perror ("getsockname");
553  close (sd);
554  return 0;
555  }
556 
557  src->s6_addr32[0] = sock6.sin6_addr.s6_addr32[0];
558  src->s6_addr32[1] = sock6.sin6_addr.s6_addr32[1];
559  src->s6_addr32[2] = sock6.sin6_addr.s6_addr32[2];
560  src->s6_addr32[3] = sock6.sin6_addr.s6_addr32[3];
561  memcpy (src, &sock6.sin6_addr, sizeof (struct in6_addr));
562  g_debug ("source addrss is %s\n",
563  inet_ntop (AF_INET6, src, name, sizeof (name)));
564  close (sd);
565  }
566  return 1; /* Calling function responsible for checking validity */
567 }
const char * name
Definition: nasl_init.c:377
Here is the caller graph for this function:

◆ v6_is_local_ip()

int v6_is_local_ip ( struct in6_addr *  )

Definition at line 117 of file pcap.c.

References getipv6routes(), ipv6addrmask(), MAXROUTES, name, and v6_getinterfaces().

Referenced by nasl_islocalnet().

118 {
119  int ifaces;
120  struct interface_info *ifs;
121  int i;
122  static struct myroute myroutes[MAXROUTES];
123  int numroutes = 0;
124  struct in6_addr in6addr;
125 
126  if ((ifs = v6_getinterfaces (&ifaces)) == NULL)
127  return -1;
128 
129  if (IN6_IS_ADDR_V4MAPPED (addr))
130  {
131  for (i = 0; i < ifaces; i++)
132  {
133  bpf_u_int32 net, mask;
134  char errbuf[PCAP_ERRBUF_SIZE];
135  pcap_lookupnet (ifs[i].name, &net, &mask, errbuf);
136  if ((net & mask) == (addr->s6_addr32[3] & mask))
137  return 1;
138  }
139  }
140  else
141  {
142  if (IN6_IS_ADDR_LINKLOCAL (addr))
143  return 1;
144  if (IN6_IS_ADDR_LOOPBACK (addr))
145  return 1;
146  if (getipv6routes (myroutes, &numroutes) == 0)
147  {
148  for (i = 0; i < numroutes; i++)
149  {
150  char addr1[INET6_ADDRSTRLEN];
151  char addr2[INET6_ADDRSTRLEN];
152 
153  memcpy (&in6addr, addr, sizeof (struct in6_addr));
154  ipv6addrmask (&in6addr, myroutes[i].mask);
155  g_debug ("comparing addresses %s and %s\n",
156  inet_ntop (AF_INET6, &in6addr, addr1, sizeof (addr1)),
157  inet_ntop (AF_INET6, &myroutes[i].dest6, addr2,
158  sizeof (addr2)));
159  if (IN6_ARE_ADDR_EQUAL (&in6addr, &myroutes[i].dest6))
160  {
161  return 1;
162  }
163  }
164  }
165  }
166  return 0;
167 }
Definition: pcap.c:58
int getipv6routes(struct myroute *myroutes, int *numroutes)
Definition: pcap.c:686
struct interface_info * v6_getinterfaces(int *howmany)
Definition: pcap.c:335
static void ipv6addrmask(struct in6_addr *in6addr, int mask)
Definition: pcap.c:74
const char * name
Definition: nasl_init.c:377
#define MAXROUTES
Definition: pcap.c:42
Here is the call graph for this function:
Here is the caller graph for this function:

◆ v6_islocalhost()

int v6_islocalhost ( struct in6_addr *  )

Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface.

Definition at line 231 of file pcap.c.

References interface_info::addr, and v6_ipaddr2devname().

Referenced by init_v6_capture_device(), nasl_islocalhost(), nasl_send_v6packet(), nasl_tcp_v6_ping(), nasl_this_host(), and v6_routethrough().

232 {
233  char dev[128];
234 
235  if (addr == NULL)
236  return -1;
237 
238  if (IN6_IS_ADDR_V4MAPPED (addr))
239  {
240  /* If it is 0.0.0.0 or starts with 127.0.0.1 then it is
241  probably localhost */
242  if ((addr->s6_addr32[3] & htonl (0xFF000000)) == htonl (0x7F000000))
243  return 1;
244 
245  if (!addr->s6_addr32[3])
246  return 1;
247  }
248 
249  if (IN6_IS_ADDR_LOOPBACK (addr))
250  return 1;
251 
252  /* If it is the same addy as a local interface, then it is
253  probably localhost */
254 
255  if (v6_ipaddr2devname (dev, sizeof (dev), addr) != -1)
256  return 1;
257 
258  /* OK, so to a first approximation, this addy is probably not
259  localhost */
260  return 0;
261 }
int v6_ipaddr2devname(char *dev, int sz, struct in6_addr *addr)
Definition: pcap.c:173
Here is the call graph for this function:
Here is the caller graph for this function:

◆ v6_routethrough()

char* v6_routethrough ( struct in6_addr *  dest,
struct in6_addr *  source 
)

An awesome function to determine what interface a packet to a given destination should be routed through.

It returns NULL if no appropriate interface is found, otherwise it returns the device name and fills in the source parameter. Some of the stuff is from Stevens' Unix Network Programming V2. He had an easier suggestion for doing this (in the book), but it isn't portable :(

Definition at line 789 of file pcap.c.

References myroute::dev, getipv4routes(), getipv6routes(), ipv6addrmask(), MAXROUTES, interface_info::name, v6_getinterfaces(), v6_getsourceip(), and v6_islocalhost().

Referenced by init_v6_capture_device(), nasl_pcap_next(), nasl_send_capture(), nasl_tcp_v6_ping(), and v6_openbpf().

790 {
791  static int initialized = 0;
792  int i;
793  struct in6_addr addy;
794  static enum {
795  procroutetechnique,
796  connectsockettechnique,
797  guesstechnique
798  } technique = procroutetechnique;
799  struct interface_info *mydevs;
800  static struct myroute myroutes[MAXROUTES];
801  int numinterfaces = 0;
802  static int numroutes = 0;
803  struct in6_addr in6addr;
804  struct in6_addr src;
805 
806  if (!dest)
807  {
808  g_message ("ipaddr2devname passed a NULL dest address");
809  return NULL;
810  }
811 
812  if (IN6_IS_ADDR_V4MAPPED (dest))
813  gvm_source_addr_as_addr6 (&src);
814  else
815  gvm_source_addr6 (&src);
816 
817  if (!initialized)
818  {
819  /* Dummy socket for ioctl */
820  initialized = 1;
821  mydevs = v6_getinterfaces (&numinterfaces);
822  if (IN6_IS_ADDR_V4MAPPED (dest))
823  {
824  if (getipv4routes (myroutes, &numroutes) < 0)
825  technique = connectsockettechnique;
826  }
827  else
828  {
829  if (getipv6routes (myroutes, &numroutes) < 0)
830  technique = connectsockettechnique;
831  }
832  }
833  else
834  {
835  mydevs = v6_getinterfaces (&numinterfaces);
836  }
837  /* WHEW, that takes care of initializing, now we have the easy job of
838  finding which route matches */
839  if (v6_islocalhost (dest))
840  {
841  if (source)
842  {
843  if (IN6_IS_ADDR_V4MAPPED (source))
844  {
845  source->s6_addr32[0] = 0;
846  source->s6_addr32[1] = 0;
847  source->s6_addr32[2] = htonl (0xffff);
848  source->s6_addr32[3] = htonl (0x7F000001);
849  }
850  else
851  {
852  source->s6_addr32[0] = 0;
853  source->s6_addr32[1] = 0;
854  source->s6_addr32[2] = 0;
855  source->s6_addr32[3] = htonl (1);
856  }
857  }
858  /* Now we find the localhost interface name, assuming 127.0.0.1
859  or ::1 is localhost (it damn well better be!)... */
860  for (i = 0; i < numinterfaces; i++)
861  {
862  if (IN6_IS_ADDR_V4MAPPED (&mydevs[i].addr6))
863  {
864  if (mydevs[i].addr6.s6_addr32[3] == htonl (0x7F000001))
865  return mydevs[i].name;
866  }
867  else
868  {
869  if (IN6_ARE_ADDR_EQUAL (&in6addr_any, &mydevs[i].addr6))
870  return mydevs[i].name;
871  }
872  }
873  return NULL;
874  }
875 
876  if (technique == procroutetechnique)
877  {
878  for (i = 0; i < numroutes; i++)
879  {
880  char addr1[INET6_ADDRSTRLEN];
881  char addr2[INET6_ADDRSTRLEN];
882 
883  memcpy (&in6addr, dest, sizeof (struct in6_addr));
884  ipv6addrmask (&in6addr, myroutes[i].mask);
885  g_debug (
886  "comparing addresses %s and %s\n",
887  inet_ntop (AF_INET6, &in6addr, addr1, sizeof (addr1)),
888  inet_ntop (AF_INET6, &myroutes[i].dest6, addr2, sizeof (addr2)));
889  if (IN6_ARE_ADDR_EQUAL (&in6addr, &myroutes[i].dest6))
890  {
891  if (source)
892  {
893  if (!IN6_ARE_ADDR_EQUAL (&src, &in6addr_any))
894  memcpy (source, &src, sizeof (struct in6_addr));
895  else
896  {
897  if (myroutes[i].dev != NULL)
898  {
899  g_debug ("copying address %s\n",
900  inet_ntop (AF_INET6, &myroutes[i].dev->addr6,
901  addr1, sizeof (addr1)));
902  g_debug ("dev name is %s\n", myroutes[i].dev->name);
903  memcpy (source, &myroutes[i].dev->addr6,
904  sizeof (struct in6_addr));
905  }
906  }
907  }
908  return myroutes[i].dev->name;
909  }
910  technique = connectsockettechnique;
911  }
912  }
913  if (technique == connectsockettechnique)
914  {
915  if (!v6_getsourceip (&addy, dest))
916  return NULL;
917  if (IN6_ARE_ADDR_EQUAL (&addy, &in6addr))
918  {
919  struct hostent *myhostent = NULL;
920  char myname[MAXHOSTNAMELEN + 1];
921 
922  myhostent = gethostbyname (myname);
923  if (gethostname (myname, MAXHOSTNAMELEN) || !myhostent)
924  g_message ("Cannot get hostname!");
925  else if (myhostent->h_addrtype == AF_INET)
926  {
927  addy.s6_addr32[0] = 0;
928  addy.s6_addr32[1] = 0;
929  addy.s6_addr32[2] = htonl (0xffff);
930  memcpy (&addy.s6_addr32[0], myhostent->h_addr_list[0],
931  sizeof (struct in6_addr));
932  }
933  else
934  memcpy (&addy, myhostent->h_addr_list[0], sizeof (struct in6_addr));
935  }
936 
937  /* Now we insure this claimed address is a real interface ... */
938  for (i = 0; i < numinterfaces; i++)
939  {
940  char addr1[INET6_ADDRSTRLEN];
941  char addr2[INET6_ADDRSTRLEN];
942 
943  g_debug (
944  "comparing addresses %s and %s\n",
945  inet_ntop (AF_INET6, &mydevs[i].addr6, addr1, sizeof (addr1)),
946  inet_ntop (AF_INET6, &addy, addr2, sizeof (addr2)));
947  if (IN6_ARE_ADDR_EQUAL (&mydevs[i].addr6, &addy))
948  {
949  if (source)
950  {
951  memcpy (source, &addy, sizeof (struct in6_addr));
952  }
953  return mydevs[i].name;
954  }
955  }
956  return NULL;
957  }
958  else
959  g_message ("%s: Provided technique is neither proc route nor"
960  " connect socket",
961  __FUNCTION__);
962  return NULL;
963 }
int v6_islocalhost(struct in6_addr *addr)
Tests whether a packet sent to IP is LIKELY to route through the kernel localhost interface...
Definition: pcap.c:231
Definition: pcap.c:58
int v6_getsourceip(struct in6_addr *src, struct in6_addr *dst)
Definition: pcap.c:478
int getipv6routes(struct myroute *myroutes, int *numroutes)
Definition: pcap.c:686
struct interface_info * v6_getinterfaces(int *howmany)
Definition: pcap.c:335
static void ipv6addrmask(struct in6_addr *in6addr, int mask)
Definition: pcap.c:74
int getipv4routes(struct myroute *myroutes, int *numroutes)
Definition: pcap.c:570
char name[64]
Definition: pcap.c:52
#define MAXROUTES
Definition: pcap.c:42
Here is the call graph for this function:
Here is the caller graph for this function: