1 /* $NetBSD: keysock.c,v 1.7 2005/02/26 22:45:13 perry Exp $ */
2 /* $FreeBSD: src/sys/netipsec/keysock.c,v 1.3.2.1 2003/01/24 05:11:36 sam Exp $ */
3 /* $KAME: keysock.c,v 1.25 2001/08/13 20:07:41 itojun Exp $ */
4
5 /*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: keysock.c,v 1.7 2005/02/26 22:45:13 perry Exp $");
36
37 #include "opt_ipsec.h"
38
39 /* This code has derived from sys/net/rtsock.c on FreeBSD2.2.5 */
40
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/domain.h>
44 #include <sys/errno.h>
45 #include <sys/kernel.h>
46 #include <sys/malloc.h>
47 #include <sys/mbuf.h>
48 #include <sys/protosw.h>
49 #include <sys/signalvar.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52 #include <sys/sysctl.h>
53 #include <sys/systm.h>
54
55 #include <net/raw_cb.h>
56 #include <net/route.h>
57
58 #include <net/pfkeyv2.h>
59 #include <netipsec/key.h>
60 #include <netipsec/keysock.h>
61 #include <netipsec/key_debug.h>
62
63 #include <netipsec/ipsec_osdep.h>
64
65 #include <machine/stdarg.h>
66
67 typedef int pr_output_t (struct mbuf *, struct socket *);
68
69 struct key_cb {
70 int key_count;
71 int any_count;
72 };
73 static struct key_cb key_cb;
74
75 static struct sockaddr key_dst = { 2, PF_KEY, };
76 static struct sockaddr key_src = { 2, PF_KEY, };
77
78
79 static int key_sendup0 __P((struct rawcb *, struct mbuf *, int, int));
80
81 struct pfkeystat pfkeystat;
82
83 int key_registered_sb_max = (NMBCLUSTERS * MHLEN); /* XXX arbitrary */
84
85 /* XXX sysctl */
86 #ifdef __FreeBSD__
87 SYSCTL_INT(_net_key, OID_AUTO, registered_sbmax, CTLFLAG_RD,
88 &key_registered_sb_max , 0, "Maximum kernel-to-user PFKEY datagram size");
89 #endif
90
91 /*
92 * key_output()
93 */
94 int
95 key_output(struct mbuf *m, ...)
96 {
97 struct sadb_msg *msg;
98 int len, error = 0;
99 int s;
100 struct socket *so;
101 va_list ap;
102
103 va_start(ap, m);
104 so = va_arg(ap, struct socket *);
105 va_end(ap);
106
107 if (m == 0)
108 panic("key_output: NULL pointer was passed.\n");
109
110 pfkeystat.out_total++;
111 pfkeystat.out_bytes += m->m_pkthdr.len;
112
113 len = m->m_pkthdr.len;
114 if (len < sizeof(struct sadb_msg)) {
115 pfkeystat.out_tooshort++;
116 error = EINVAL;
117 goto end;
118 }
119
120 if (m->m_len < sizeof(struct sadb_msg)) {
121 if ((m = m_pullup(m, sizeof(struct sadb_msg))) == 0) {
122 pfkeystat.out_nomem++;
123 error = ENOBUFS;
124 goto end;
125 }
126 }
127
128 if ((m->m_flags & M_PKTHDR) == 0)
129 panic("key_output: not M_PKTHDR ??");
130
131 KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m));
132
133 msg = mtod(m, struct sadb_msg *);
134 pfkeystat.out_msgtype[msg->sadb_msg_type]++;
135 if (len != PFKEY_UNUNIT64(msg->sadb_msg_len)) {
136 pfkeystat.out_invlen++;
137 error = EINVAL;
138 goto end;
139 }
140
141 /*XXX giant lock*/
142 s = splsoftnet();
143 error = key_parse(m, so);
144 m = NULL;
145 splx(s);
146 end:
147 if (m)
148 m_freem(m);
149 return error;
150 }
151
152 /*
153 * send message to the socket.
154 */
155 static int
156 key_sendup0(rp, m, promisc, sbprio)
157 struct rawcb *rp;
158 struct mbuf *m;
159 int promisc;
160 {
161 int error;
162 int ok;
163
164 if (promisc) {
165 struct sadb_msg *pmsg;
166
167 M_PREPEND(m, sizeof(struct sadb_msg), M_DONTWAIT);
168 if (m && m->m_len < sizeof(struct sadb_msg))
169 m = m_pullup(m, sizeof(struct sadb_msg));
170 if (!m) {
171 pfkeystat.in_nomem++;
172 m_freem(m);
173 return ENOBUFS;
174 }
175 m->m_pkthdr.len += sizeof(*pmsg);
176
177 pmsg = mtod(m, struct sadb_msg *);
178 bzero(pmsg, sizeof(*pmsg));
179 pmsg->sadb_msg_version = PF_KEY_V2;
180 pmsg->sadb_msg_type = SADB_X_PROMISC;
181 pmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len);
182 /* pid and seq? */
183
184 pfkeystat.in_msgtype[pmsg->sadb_msg_type]++;
185 }
186
187 if (sbprio == 0)
188 ok = sbappendaddr(&rp->rcb_socket->so_rcv,
189 (struct sockaddr *)&key_src, m, NULL);
190 else
191 ok = sbappendaddrchain(&rp->rcb_socket->so_rcv,
192 (struct sockaddr *)&key_src, m, sbprio);
193
194 if (!ok) {
195 pfkeystat.in_nomem++;
196 m_freem(m);
197 error = ENOBUFS;
198 } else
199 error = 0;
200 sorwakeup(rp->rcb_socket);
201 return error;
202 }
203
204 /* XXX this interface should be obsoleted. */
205 int
206 key_sendup(so, msg, len, target)
207 struct socket *so;
208 struct sadb_msg *msg;
209 u_int len;
210 int target; /*target of the resulting message*/
211 {
212 struct mbuf *m, *n, *mprev;
213 int tlen;
214
215 /* sanity check */
216 if (so == 0 || msg == 0)
217 panic("key_sendup: NULL pointer was passed.\n");
218
219 KEYDEBUG(KEYDEBUG_KEY_DUMP,
220 printf("key_sendup: \n");
221 kdebug_sadb(msg));
222
223 /*
224 * we increment statistics here, just in case we have ENOBUFS
225 * in this function.
226 */
227 pfkeystat.in_total++;
228 pfkeystat.in_bytes += len;
229 pfkeystat.in_msgtype[msg->sadb_msg_type]++;
230
231 /*
232 * Get mbuf chain whenever possible (not clusters),
233 * to save socket buffer. We'll be generating many SADB_ACQUIRE
234 * messages to listening key sockets. If we simply allocate clusters,
235 * sbappendaddr() will raise ENOBUFS due to too little sbspace().
236 * sbspace() computes # of actual data bytes AND mbuf region.
237 *
238 * TODO: SADB_ACQUIRE filters should be implemented.
239 */
240 tlen = len;
241 m = mprev = NULL;
242 while (tlen > 0) {
243 if (tlen == len) {
244 MGETHDR(n, M_DONTWAIT, MT_DATA);
245 n->m_len = MHLEN;
246 } else {
247 MGET(n, M_DONTWAIT, MT_DATA);
248 n->m_len = MLEN;
249 }
250 if (!n) {
251 pfkeystat.in_nomem++;
252 return ENOBUFS;
253 }
254 if (tlen >= MCLBYTES) { /*XXX better threshold? */
255 MCLGET(n, M_DONTWAIT);
256 if ((n->m_flags & M_EXT) == 0) {
257 m_free(n);
258 m_freem(m);
259 pfkeystat.in_nomem++;
260 return ENOBUFS;
261 }
262 n->m_len = MCLBYTES;
263 }
264
265 if (tlen < n->m_len)
266 n->m_len = tlen;
267 n->m_next = NULL;
268 if (m == NULL)
269 m = mprev = n;
270 else {
271 mprev->m_next = n;
272 mprev = n;
273 }
274 tlen -= n->m_len;
275 n = NULL;
276 }
277 m->m_pkthdr.len = len;
278 m->m_pkthdr.rcvif = NULL;
279 m_copyback(m, 0, len, (caddr_t)msg);
280
281 /* avoid duplicated statistics */
282 pfkeystat.in_total--;
283 pfkeystat.in_bytes -= len;
284 pfkeystat.in_msgtype[msg->sadb_msg_type]--;
285
286 return key_sendup_mbuf(so, m, target);
287 }
288
289 /* so can be NULL if target != KEY_SENDUP_ONE */
290 int
291 key_sendup_mbuf(so, m, target /*, sbprio */)
292 struct socket *so;
293 struct mbuf *m;
294 int target;
295 {
296 struct mbuf *n;
297 struct keycb *kp;
298 int sendup;
299 struct rawcb *rp;
300 int error = 0;
301 int sbprio = 0; /* XXX should be a parameter */
302
303 if (m == NULL)
304 panic("key_sendup_mbuf: NULL pointer was passed.\n");
305 if (so == NULL && target == KEY_SENDUP_ONE)
306 panic("key_sendup_mbuf: NULL pointer was passed.\n");
307
308 /*
309 * RFC 2367 says ACQUIRE and other kernel-generated messages
310 * are special. We treat all KEY_SENDUP_REGISTERED messages
311 * as special, delivering them to all registered sockets
312 * even if the socket is at or above its so->so_rcv.sb_max limits.
313 * The only constraint is that the so_rcv data fall below
314 * key_registered_sb_max.
315 * Doing that check here avoids reworking every key_sendup_mbuf()
316 * in the short term. . The rework will be done after a technical
317 * conensus that this approach is appropriate.
318 */
319 if (target == KEY_SENDUP_REGISTERED) {
320 sbprio = SB_PRIO_BESTEFFORT;
321 }
322
323 pfkeystat.in_total++;
324 pfkeystat.in_bytes += m->m_pkthdr.len;
325 if (m->m_len < sizeof(struct sadb_msg)) {
326 #if 1
327 m = m_pullup(m, sizeof(struct sadb_msg));
328 if (m == NULL) {
329 pfkeystat.in_nomem++;
330 return ENOBUFS;
331 }
332 #else
333 /* don't bother pulling it up just for stats */
334 #endif
335 }
336 if (m->m_len >= sizeof(struct sadb_msg)) {
337 struct sadb_msg *msg;
338 msg = mtod(m, struct sadb_msg *);
339 pfkeystat.in_msgtype[msg->sadb_msg_type]++;
340 }
341
342 LIST_FOREACH(rp, &rawcb_list, rcb_list)
343 {
344 struct socket * kso = rp->rcb_socket;
345 if (rp->rcb_proto.sp_family != PF_KEY)
346 continue;
347 if (rp->rcb_proto.sp_protocol
348 && rp->rcb_proto.sp_protocol != PF_KEY_V2) {
349 continue;
350 }
351
352 kp = (struct keycb *)rp;
353
354 /*
355 * If you are in promiscuous mode, and when you get broadcasted
356 * reply, you'll get two PF_KEY messages.
357 * (based on pf_key@inner.net message on 14 Oct 1998)
358 */
359 if (((struct keycb *)rp)->kp_promisc) {
360 if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
361 (void)key_sendup0(rp, n, 1, 0);
362 n = NULL;
363 }
364 }
365
366 /* the exact target will be processed later */
367 if (so && sotorawcb(so) == rp)
368 continue;
369
370 sendup = 0;
371 switch (target) {
372 case KEY_SENDUP_ONE:
373 /* the statement has no effect */
374 if (so && sotorawcb(so) == rp)
375 sendup++;
376 break;
377 case KEY_SENDUP_ALL:
378 sendup++;
379 break;
380 case KEY_SENDUP_REGISTERED:
381 if (kp->kp_registered) {
382 if (kso->so_rcv.sb_cc <= key_registered_sb_max)
383 sendup++;
384 else
385 printf("keysock: "
386 "registered sendup dropped, "
387 "sb_cc %ld max %d\n",
388 kso->so_rcv.sb_cc,
389 key_registered_sb_max);
390 }
391 break;
392 }
393 pfkeystat.in_msgtarget[target]++;
394
395 if (!sendup)
396 continue;
397
398 if ((n = m_copy(m, 0, (int)M_COPYALL)) == NULL) {
399 m_freem(m);
400 pfkeystat.in_nomem++;
401 return ENOBUFS;
402 }
403
404 if ((error = key_sendup0(rp, n, 0, 0)) != 0) {
405 m_freem(m);
406 return error;
407 }
408
409 n = NULL;
410 }
411
412 /* The 'later' time for processing the exact target has arrived */
413 if (so) {
414 error = key_sendup0(sotorawcb(so), m, 0, sbprio);
415 m = NULL;
416 } else {
417 error = 0;
418 m_freem(m);
419 }
420 return error;
421 }
422
423 #ifdef __FreeBSD__
424
425 /*
426 * key_abort()
427 * derived from net/rtsock.c:rts_abort()
428 */
429 static int
430 key_abort(struct socket *so)
431 {
432 int s, error;
433 s = splnet(); /* FreeBSD */
434 error = raw_usrreqs.pru_abort(so);
435 splx(s);
436 return error;
437 }
438
439 /*
440 * key_attach()
441 * derived from net/rtsock.c:rts_attach()
442 */
443 static int
444 key_attach(struct socket *so, int proto, struct proc *td)
445 {
446 struct keycb *kp;
447 int s, error;
448
449 if (sotorawcb(so) != 0)
450 return EISCONN; /* XXX panic? */
451 kp = (struct keycb *)malloc(sizeof *kp, M_PCB, M_WAITOK|M_ZERO); /* XXX */
452 if (kp == 0)
453 return ENOBUFS;
454
455 /*
456 * The spl[soft]net() is necessary to block protocols from sending
457 * error notifications (like RTM_REDIRECT or RTM_LOSING) while
458 * this PCB is extant but incompletely initialized.
459 * Probably we should try to do more of this work beforehand and
460 * eliminate the spl.
461 */
462 s = splnet(); /* FreeBSD */
463 so->so_pcb = (caddr_t)kp;
464 error = raw_usrreqs.pru_attach(so, proto, td);
465 kp = (struct keycb *)sotorawcb(so);
466 if (error) {
467 free(kp, M_PCB);
468 so->so_pcb = (caddr_t) 0;
469 splx(s);
470 return error;
471 }
472
473 kp->kp_promisc = kp->kp_registered = 0;
474
475 if (kp->kp_raw.rcb_proto.sp_protocol == PF_KEY) /* XXX: AF_KEY */
476 key_cb.key_count++;
477 key_cb.any_count++;
478 kp->kp_raw.rcb_laddr = &key_src;
479 kp->kp_raw.rcb_faddr = &key_dst;
480 soisconnected(so);
481 so->so_options |= SO_USELOOPBACK;
482
483 splx(s);
484 return 0;
485 }
486
487 /*
488 * key_bind()
489 * derived from net/rtsock.c:rts_bind()
490 */
491 static int
492 key_bind(struct socket *so, struct sockaddr *nam, struct proc *td)
493 {
494 int s, error;
495 s = splnet(); /* FreeBSD */
496 error = raw_usrreqs.pru_bind(so, nam, td); /* xxx just EINVAL */
497 splx(s);
498 return error;
499 }
500
501 /*
502 * key_connect()
503 * derived from net/rtsock.c:rts_connect()
504 */
505 static int
506 key_connect(struct socket *so, struct sockaddr *nam, struct proc *td)
507 {
508 int s, error;
509 s = splnet(); /* FreeBSD */
510 error = raw_usrreqs.pru_connect(so, nam, td); /* XXX just EINVAL */
511 splx(s);
512 return error;
513 }
514
515 /*
516 * key_detach()
517 * derived from net/rtsock.c:rts_detach()
518 */
519 static int
520 key_detach(struct socket *so)
521 {
522 struct keycb *kp = (struct keycb *)sotorawcb(so);
523 int s, error;
524
525 s = splnet(); /* FreeBSD */
526 if (kp != 0) {
527 if (kp->kp_raw.rcb_proto.sp_protocol
528 == PF_KEY) /* XXX: AF_KEY */
529 key_cb.key_count--;
530 key_cb.any_count--;
531
532 key_freereg(so);
533 }
534 error = raw_usrreqs.pru_detach(so);
535 splx(s);
536 return error;
537 }
538
539 /*
540 * key_disconnect()
541 * derived from net/rtsock.c:key_disconnect()
542 */
543 static int
544 key_disconnect(struct socket *so)
545 {
546 int s, error;
547 s = splnet(); /* FreeBSD */
548 error = raw_usrreqs.pru_disconnect(so);
549 splx(s);
550 return error;
551 }
552
553 /*
554 * key_peeraddr()
555 * derived from net/rtsock.c:rts_peeraddr()
556 */
557 static int
558 key_peeraddr(struct socket *so, struct sockaddr **nam)
559 {
560 int s, error;
561 s = splnet(); /* FreeBSD */
562 error = raw_usrreqs.pru_peeraddr(so, nam);
563 splx(s);
564 return error;
565 }
566
567 /*
568 * key_send()
569 * derived from net/rtsock.c:rts_send()
570 */
571 static int
572 key_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
573 struct mbuf *control, struct proc *td)
574 {
575 int s, error;
576 s = splnet(); /* FreeBSD */
577 error = raw_usrreqs.pru_send(so, flags, m, nam, control, td);
578 splx(s);
579 return error;
580 }
581
582 /*
583 * key_shutdown()
584 * derived from net/rtsock.c:rts_shutdown()
585 */
586 static int
587 key_shutdown(struct socket *so)
588 {
589 int s, error;
590 s = splnet(); /* FreeBSD */
591 error = raw_usrreqs.pru_shutdown(so);
592 splx(s);
593 return error;
594 }
595
596 /*
597 * key_sockaddr()
598 * derived from net/rtsock.c:rts_sockaddr()
599 */
600 static int
601 key_sockaddr(struct socket *so, struct sockaddr **nam)
602 {
603 int s, error;
604 s = splnet(); /* FreeBSD */
605 error = raw_usrreqs.pru_sockaddr(so, nam);
606 splx(s);
607 return error;
608 }
609 #else /*!__FreeBSD__ -- traditional proto_usrreq() switch */
610
611 /*
612 * key_usrreq()
613 * derived from net/rtsock.c:route_usrreq()
614 */
615 int
616 key_usrreq(so, req, m, nam, control, p)
617 struct socket *so;
618 int req;
619 struct mbuf *m, *nam, *control;
620 struct proc *p;
621 {
622 int error = 0;
623 struct keycb *kp = (struct keycb *)sotorawcb(so);
624 int s;
625
626 s = splsoftnet();
627 if (req == PRU_ATTACH) {
628 kp = (struct keycb *)malloc(sizeof(*kp), M_PCB, M_WAITOK);
629 so->so_pcb = (caddr_t)kp;
630 if (so->so_pcb)
631 bzero(so->so_pcb, sizeof(*kp));
632 }
633 if (req == PRU_DETACH && kp) {
634 int af = kp->kp_raw.rcb_proto.sp_protocol;
635 if (af == PF_KEY) /* XXX: AF_KEY */
636 key_cb.key_count--;
637 key_cb.any_count--;
638
639 key_freereg(so);
640 }
641
642 error = raw_usrreq(so, req, m, nam, control, p);
643 m = control = NULL; /* reclaimed in raw_usrreq */
644 kp = (struct keycb *)sotorawcb(so);
645 if (req == PRU_ATTACH && kp) {
646 int af = kp->kp_raw.rcb_proto.sp_protocol;
647 if (error) {
648 pfkeystat.sockerr++;
649 free((caddr_t)kp, M_PCB);
650 so->so_pcb = (caddr_t) 0;
651 splx(s);
652 return (error);
653 }
654
655 kp->kp_promisc = kp->kp_registered = 0;
656
657 if (af == PF_KEY) /* XXX: AF_KEY */
658 key_cb.key_count++;
659 key_cb.any_count++;
660 kp->kp_raw.rcb_laddr = &key_src;
661 kp->kp_raw.rcb_faddr = &key_dst;
662 soisconnected(so);
663 so->so_options |= SO_USELOOPBACK;
664 }
665 splx(s);
666 return (error);
667 }
668 #endif /*!__FreeBSD__*/
669
670 /* sysctl */
671 #ifdef SYSCTL_NODE
672 SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW, 0, "Key Family");
673 #endif /* SYSCTL_NODE */
674
675 /*
676 * Definitions of protocols supported in the KEY domain.
677 */
678
679 #ifdef __FreeBSD__
680 extern struct domain keydomain;
681
682 struct pr_usrreqs key_usrreqs = {
683 key_abort, pru_accept_notsupp, key_attach, key_bind,
684 key_connect,
685 pru_connect2_notsupp, pru_control_notsupp, key_detach,
686 key_disconnect, pru_listen_notsupp, key_peeraddr,
687 pru_rcvd_notsupp,
688 pru_rcvoob_notsupp, key_send, pru_sense_null, key_shutdown,
689 key_sockaddr, sosend, soreceive, sopoll
690 };
691
692 struct protosw keysw[] = {
693 { SOCK_RAW, &keydomain, PF_KEY_V2, PR_ATOMIC|PR_ADDR,
694 0, (pr_output_t *)key_output, raw_ctlinput, 0,
695 0,
696 raw_init, 0, 0, 0,
697 &key_usrreqs
698 }
699 };
700
701 static void
702 key_init0(void)
703 {
704 bzero((caddr_t)&key_cb, sizeof(key_cb));
705 key_init();
706 }
707
708 struct domain keydomain =
709 { PF_KEY, "key", key_init0, 0, 0,
710 keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
711
712 DOMAIN_SET(key);
713
714 #else /* !__FreeBSD__ */
715
716 DOMAIN_DEFINE(keydomain);
717
718 struct protosw keysw[] = {
719 { SOCK_RAW, &keydomain, PF_KEY_V2, PR_ATOMIC|PR_ADDR,
720 0, key_output, raw_ctlinput, 0,
721 key_usrreq,
722 raw_init, 0, 0, 0,
723 NULL,
724 }
725 };
726
727 struct domain keydomain =
728 { PF_KEY, "key", key_init, 0, 0,
729 keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
730
731 #endif
Cache object: 1b56134483ab51aea170ff47acb342c8
|