]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
build: remove $Format tags
[mirror_frr.git] / pimd / pim_mroute.c
1 /*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22 #include "log.h"
23 #include "privs.h"
24 #include "if.h"
25 #include "prefix.h"
26
27 #include "pimd.h"
28 #include "pim_mroute.h"
29 #include "pim_oil.h"
30 #include "pim_str.h"
31 #include "pim_time.h"
32 #include "pim_iface.h"
33 #include "pim_macro.h"
34 #include "pim_rp.h"
35 #include "pim_oil.h"
36 #include "pim_register.h"
37
38 /* GLOBAL VARS */
39 extern struct zebra_privs_t pimd_privs;
40
41 static void mroute_read_on(void);
42
43 static int pim_mroute_set(int fd, int enable)
44 {
45 int err;
46 int opt = enable ? MRT_INIT : MRT_DONE;
47 socklen_t opt_len = sizeof(opt);
48
49 err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
50 if (err) {
51 int e = errno;
52 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
53 __FILE__, __PRETTY_FUNCTION__,
54 fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
55 errno = e;
56 return -1;
57 }
58
59 #if 0
60 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
61 __FILE__, __PRETTY_FUNCTION__,
62 fd, opt);
63 #endif
64
65 return 0;
66 }
67
68 static int
69 pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src)
70 {
71 struct listnode *cnode;
72 struct connected *c;
73 struct prefix p;
74
75 p.family = AF_INET;
76 p.u.prefix4 = src;
77 p.prefixlen = IPV4_MAX_BITLEN;
78
79 for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
80 {
81 if ((c->address->family == AF_INET) &&
82 prefix_match (CONNECTED_PREFIX (c), &p))
83 {
84 return 1;
85 }
86 }
87
88 return 0;
89 }
90
91 static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = {
92 "<unknown_upcall?>",
93 "NOCACHE",
94 "WRONGVIF",
95 "WHOLEPKT", };
96
97 static int
98 pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg,
99 const char *src_str, const char *grp_str)
100 {
101 struct pim_interface *pim_ifp = ifp->info;
102 struct pim_upstream *up;
103 struct pim_rpf *rpg;
104
105 rpg = RP(msg->im_dst);
106 /*
107 * If the incoming interface is unknown OR
108 * the Interface type is SSM we don't need to
109 * do anything here
110 */
111 if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
112 (!pim_ifp) ||
113 (!(PIM_I_am_DR(pim_ifp))) ||
114 (pim_ifp->itype == PIM_INTERFACE_SSM))
115 return 0;
116
117 /*
118 * If we've received a multicast packet that isn't connected to
119 * us
120 */
121 if (!pim_mroute_connected_to_source (ifp, msg->im_src))
122 {
123 if (PIM_DEBUG_PIM_TRACE)
124 zlog_debug ("%s: Received incoming packet that does originate on our seg",
125 __PRETTY_FUNCTION__);
126 return 0;
127 }
128
129 if (PIM_DEBUG_PIM_TRACE) {
130 zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
131 __PRETTY_FUNCTION__, grp_str, src_str);
132 }
133
134 up = pim_upstream_add(msg->im_src, msg->im_dst, ifp);
135 if (!up) {
136 if (PIM_DEBUG_PIM_TRACE) {
137 zlog_debug("%s: Failure to add upstream information for (%s,%s)",
138 __PRETTY_FUNCTION__,
139 src_str, grp_str);
140 }
141 return 0;
142 }
143
144 pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
145
146 up->channel_oil = pim_channel_oil_add(msg->im_dst,
147 msg->im_src,
148 pim_ifp->mroute_vif_index);
149 if (!up->channel_oil) {
150 if (PIM_DEBUG_PIM_TRACE) {
151 zlog_debug("%s: Failure to add channel oil for (%s,%s)",
152 __PRETTY_FUNCTION__,
153 src_str, grp_str);
154 }
155 return 0;
156 }
157 up->channel_oil->cc.pktcnt++;
158
159 pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE);
160
161 return 0;
162 }
163
164 static int
165 pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf,
166 const char *src_str, const char *grp_str)
167 {
168 struct pim_interface *pim_ifp;
169 struct in_addr group;
170 struct in_addr src;
171 struct pim_rpf *rpg;
172 const struct ip *ip_hdr;
173 struct pim_upstream *up;
174
175 ip_hdr = (const struct ip *)buf;
176
177 src = ip_hdr->ip_src;
178 group = ip_hdr->ip_dst;
179
180 up = pim_upstream_find(src, group);
181 if (!up) {
182 if (PIM_DEBUG_PIM_TRACE) {
183 zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
184 __PRETTY_FUNCTION__, src_str, grp_str);
185 }
186 return 0;
187 }
188
189 pim_ifp = up->rpf.source_nexthop.interface->info;
190
191 rpg = RP(group);
192
193 if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
194 (!pim_ifp) ||
195 (!(PIM_I_am_DR(pim_ifp))) ||
196 (pim_ifp->itype == PIM_INTERFACE_SSM)) {
197 if (PIM_DEBUG_PIM_TRACE) {
198 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__);
199 }
200 return 0;
201 }
202
203 pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg);
204 return 0;
205 }
206
207 static int
208 pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg,
209 const char *src_str, const char *grp_str)
210 {
211 struct pim_ifchannel *ch;
212 struct pim_interface *pim_ifp;
213
214 /*
215 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
216
217 RFC 4601 4.8.2. PIM-SSM-Only Routers
218
219 iif is the incoming interface of the packet.
220 if (iif is in inherited_olist(S,G)) {
221 send Assert(S,G) on iif
222 }
223 */
224
225 if (!ifp) {
226 if (PIM_DEBUG_PIM_TRACE) {
227 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
228 __PRETTY_FUNCTION__,
229 src_str, grp_str, msg->im_vif);
230 }
231 return -1;
232 }
233
234 pim_ifp = ifp->info;
235 if (!pim_ifp) {
236 if (PIM_DEBUG_PIM_TRACE) {
237 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
238 __PRETTY_FUNCTION__,
239 src_str, grp_str, ifp->name);
240 }
241 return -2;
242 }
243
244 ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
245 if (!ch) {
246 if (PIM_DEBUG_PIM_TRACE) {
247 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
248 __PRETTY_FUNCTION__,
249 src_str, grp_str, ifp->name);
250 }
251 return -3;
252 }
253
254 /*
255 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
256
257 Transitions from NoInfo State
258
259 An (S,G) data packet arrives on interface I, AND
260 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
261 downstream interface that is in our (S,G) outgoing interface
262 list. We optimistically assume that we will be the assert
263 winner for this (S,G), and so we transition to the "I am Assert
264 Winner" state and perform Actions A1 (below), which will
265 initiate the assert negotiation for (S,G).
266 */
267
268 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
269 if (PIM_DEBUG_PIM_TRACE) {
270 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
271 __PRETTY_FUNCTION__,
272 src_str, grp_str, ifp->name);
273 }
274 return -4;
275 }
276
277 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
278 if (PIM_DEBUG_PIM_TRACE) {
279 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
280 __PRETTY_FUNCTION__,
281 src_str, grp_str, ifp->name);
282 }
283 return -5;
284 }
285
286 if (assert_action_a1(ch)) {
287 if (PIM_DEBUG_PIM_TRACE) {
288 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
289 __PRETTY_FUNCTION__,
290 src_str, grp_str, ifp->name);
291 }
292 return -6;
293 }
294
295 return 0;
296 }
297
298 int pim_mroute_msg(int fd, const char *buf, int buf_size)
299 {
300 struct interface *ifp;
301 const struct ip *ip_hdr;
302 const struct igmpmsg *msg;
303 char src_str[100] = "<src?>";
304 char grp_str[100] = "<grp?>";
305
306 ip_hdr = (const struct ip *) buf;
307
308 /* kernel upcall must have protocol=0 */
309 if (ip_hdr->ip_p) {
310 /* this is not a kernel upcall */
311 if (PIM_DEBUG_PIM_TRACE) {
312 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
313 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
314 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
315 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
316 }
317 return 0;
318 }
319
320 msg = (const struct igmpmsg *) buf;
321
322 ifp = pim_if_find_by_vif_index(msg->im_vif);
323
324 if (PIM_DEBUG_PIM_TRACE) {
325 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
326 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
327 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
328 __PRETTY_FUNCTION__,
329 igmpmsgtype2str[msg->im_msgtype],
330 msg->im_msgtype,
331 ip_hdr->ip_p,
332 fd,
333 src_str,
334 grp_str,
335 ifp ? ifp->name : "<ifname?>",
336 msg->im_vif);
337 }
338
339 switch (msg->im_msgtype) {
340 case IGMPMSG_WRONGVIF:
341 return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str);
342 break;
343 case IGMPMSG_NOCACHE:
344 return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str);
345 break;
346 case IGMPMSG_WHOLEPKT:
347 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str);
348 break;
349 default:
350 break;
351 }
352
353 return 0;
354 }
355
356 static int mroute_read_msg(int fd)
357 {
358 const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
359 char buf[1000];
360 int rd;
361
362 if (((int) sizeof(buf)) < msg_min_size) {
363 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
364 __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
365 return -1;
366 }
367
368 rd = read(fd, buf, sizeof(buf));
369 if (rd < 0) {
370 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
371 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
372 return -2;
373 }
374
375 if (rd < msg_min_size) {
376 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
377 __PRETTY_FUNCTION__, fd, rd, msg_min_size);
378 return -3;
379 }
380
381 return pim_mroute_msg(fd, buf, rd);
382 }
383
384 static int mroute_read(struct thread *t)
385 {
386 int fd;
387 int result;
388
389 zassert(t);
390 zassert(!THREAD_ARG(t));
391
392 fd = THREAD_FD(t);
393 zassert(fd == qpim_mroute_socket_fd);
394
395 result = mroute_read_msg(fd);
396
397 /* Keep reading */
398 qpim_mroute_socket_reader = 0;
399 mroute_read_on();
400
401 return result;
402 }
403
404 static void mroute_read_on()
405 {
406 zassert(!qpim_mroute_socket_reader);
407 zassert(PIM_MROUTE_IS_ENABLED);
408
409 THREAD_READ_ON(master, qpim_mroute_socket_reader,
410 mroute_read, 0, qpim_mroute_socket_fd);
411 }
412
413 static void mroute_read_off()
414 {
415 THREAD_OFF(qpim_mroute_socket_reader);
416 }
417
418 int pim_mroute_socket_enable()
419 {
420 int fd;
421
422 if (PIM_MROUTE_IS_ENABLED)
423 return -1;
424
425 if ( pimd_privs.change (ZPRIVS_RAISE) )
426 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
427 safe_strerror (errno) );
428
429 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
430
431 if ( pimd_privs.change (ZPRIVS_LOWER) )
432 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
433 safe_strerror (errno) );
434
435 if (fd < 0) {
436 zlog_warn("Could not create mroute socket: errno=%d: %s",
437 errno, safe_strerror(errno));
438 return -2;
439 }
440
441 if (pim_mroute_set(fd, 1)) {
442 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
443 fd, errno, safe_strerror(errno));
444 close(fd);
445 return -3;
446 }
447
448 qpim_mroute_socket_fd = fd;
449
450 qpim_mroute_socket_creation = pim_time_monotonic_sec();
451 mroute_read_on();
452
453 zassert(PIM_MROUTE_IS_ENABLED);
454
455 return 0;
456 }
457
458 int pim_mroute_socket_disable()
459 {
460 if (PIM_MROUTE_IS_DISABLED)
461 return -1;
462
463 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
464 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
465 qpim_mroute_socket_fd, errno, safe_strerror(errno));
466 return -2;
467 }
468
469 if (close(qpim_mroute_socket_fd)) {
470 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
471 qpim_mroute_socket_fd, errno, safe_strerror(errno));
472 return -3;
473 }
474
475 mroute_read_off();
476 qpim_mroute_socket_fd = -1;
477
478 zassert(PIM_MROUTE_IS_DISABLED);
479
480 return 0;
481 }
482
483 /*
484 For each network interface (e.g., physical or a virtual tunnel) that
485 would be used for multicast forwarding, a corresponding multicast
486 interface must be added to the kernel.
487 */
488 int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
489 {
490 struct pim_interface *pim_ifp = ifp->info;
491 struct vifctl vc;
492 int err;
493
494 if (PIM_MROUTE_IS_DISABLED) {
495 zlog_warn("%s: global multicast is disabled",
496 __PRETTY_FUNCTION__);
497 return -1;
498 }
499
500 memset(&vc, 0, sizeof(vc));
501 vc.vifc_vifi = pim_ifp->mroute_vif_index;
502 #ifdef VIFF_USE_IFINDEX
503 vc.vifc_lcl_ifindex = ifp->ifindex;
504 #else
505 if (ifaddr.s_addr == INADDR_ANY) {
506 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
507 __PRETTY_FUNCTION__);
508 return -1;
509 }
510 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
511 #endif
512 vc.vifc_flags = flags;
513 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
514 vc.vifc_rate_limit = 0;
515
516 #ifdef PIM_DVMRP_TUNNEL
517 if (vc.vifc_flags & VIFF_TUNNEL) {
518 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
519 }
520 #endif
521
522 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
523 if (err) {
524 char ifaddr_str[100];
525 int e = errno;
526
527 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
528
529 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
530 __FILE__, __PRETTY_FUNCTION__,
531 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
532 e, safe_strerror(e));
533 errno = e;
534 return -2;
535 }
536
537 return 0;
538 }
539
540 int pim_mroute_del_vif(int vif_index)
541 {
542 struct vifctl vc;
543 int err;
544
545 if (PIM_MROUTE_IS_DISABLED) {
546 zlog_warn("%s: global multicast is disabled",
547 __PRETTY_FUNCTION__);
548 return -1;
549 }
550
551 memset(&vc, 0, sizeof(vc));
552 vc.vifc_vifi = vif_index;
553
554 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
555 if (err) {
556 int e = errno;
557 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
558 __FILE__, __PRETTY_FUNCTION__,
559 qpim_mroute_socket_fd, vif_index,
560 e, safe_strerror(e));
561 errno = e;
562 return -2;
563 }
564
565 return 0;
566 }
567
568 int pim_mroute_add(struct channel_oil *c_oil)
569 {
570 int err;
571 int orig = 0;
572
573 qpim_mroute_add_last = pim_time_monotonic_sec();
574 ++qpim_mroute_add_events;
575
576 if (PIM_MROUTE_IS_DISABLED) {
577 zlog_warn("%s: global multicast is disabled",
578 __PRETTY_FUNCTION__);
579 return -1;
580 }
581
582 /* The linux kernel *expects* the incoming
583 * vif to be part of the outgoing list
584 * in the case of a (*,G).
585 */
586 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
587 {
588 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
589 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
590 }
591
592 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
593 &c_oil->oil, sizeof(c_oil->oil));
594
595 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
596 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
597
598 if (err) {
599 int e = errno;
600 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
601 __FILE__, __PRETTY_FUNCTION__,
602 qpim_mroute_socket_fd,
603 e, safe_strerror(e));
604 errno = e;
605 return -2;
606 }
607
608 c_oil->installed = 1;
609 return 0;
610 }
611
612 int pim_mroute_del (struct channel_oil *c_oil)
613 {
614 int err;
615
616 qpim_mroute_del_last = pim_time_monotonic_sec();
617 ++qpim_mroute_del_events;
618
619 if (PIM_MROUTE_IS_DISABLED) {
620 zlog_warn("%s: global multicast is disabled",
621 __PRETTY_FUNCTION__);
622 return -1;
623 }
624
625 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
626 if (err) {
627 int e = errno;
628 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
629 __FILE__, __PRETTY_FUNCTION__,
630 qpim_mroute_socket_fd,
631 e, safe_strerror(e));
632 errno = e;
633 return -2;
634 }
635
636 c_oil->installed = 0;
637
638 return 0;
639 }
640
641 void
642 pim_mroute_update_counters (struct channel_oil *c_oil)
643 {
644 struct sioc_sg_req sgreq;
645
646 memset (&sgreq, 0, sizeof(sgreq));
647 sgreq.src = c_oil->oil.mfcc_origin;
648 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
649
650 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
651 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
652 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
653
654 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
655 {
656 char group_str[100];
657 char source_str[100];
658
659 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
660 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
661
662 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
663 (unsigned long)SIOCGETSGCNT,
664 source_str,
665 group_str,
666 errno,
667 safe_strerror(errno));
668 return;
669 }
670
671 c_oil->cc.pktcnt = sgreq.pktcnt;
672 c_oil->cc.bytecnt = sgreq.bytecnt;
673 c_oil->cc.wrong_if = sgreq.wrong_if;
674
675 return;
676 }