3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
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,
34 #include "pim_iface.h"
35 #include "pim_hello.h"
36 #include "pim_macro.h"
37 #include "pim_assert.h"
38 #include "pim_ifchannel.h"
40 static int assert_action_a3(struct pim_ifchannel
*ch
);
41 static void assert_action_a2(struct pim_ifchannel
*ch
,
42 struct pim_assert_metric winner_metric
);
43 static void assert_action_a6(struct pim_ifchannel
*ch
,
44 struct pim_assert_metric winner_metric
);
46 void pim_ifassert_winner_set(struct pim_ifchannel
*ch
,
47 enum pim_ifassert_state new_state
,
48 struct in_addr winner
,
49 struct pim_assert_metric winner_metric
)
51 int winner_changed
= (ch
->ifassert_winner
.s_addr
!= winner
.s_addr
);
52 int metric_changed
= !pim_assert_metric_match(&ch
->ifassert_winner_metric
,
55 if (PIM_DEBUG_PIM_EVENTS
) {
56 if (ch
->ifassert_state
!= new_state
) {
57 zlog_debug("%s: (S,G)=%s assert state changed from %s to %s on interface %s",
60 pim_ifchannel_ifassert_name(ch
->ifassert_state
),
61 pim_ifchannel_ifassert_name(new_state
),
66 char was_str
[INET_ADDRSTRLEN
];
67 char winner_str
[INET_ADDRSTRLEN
];
68 pim_inet4_dump("<was?>", ch
->ifassert_winner
, was_str
, sizeof(was_str
));
69 pim_inet4_dump("<winner?>", winner
, winner_str
, sizeof(winner_str
));
70 zlog_debug("%s: (S,G)=%s assert winner changed from %s to %s on interface %s",
73 was_str
, winner_str
, ch
->interface
->name
);
75 } /* PIM_DEBUG_PIM_EVENTS */
77 ch
->ifassert_state
= new_state
;
78 ch
->ifassert_winner
= winner
;
79 ch
->ifassert_winner_metric
= winner_metric
;
80 ch
->ifassert_creation
= pim_time_monotonic_sec();
82 if (winner_changed
|| metric_changed
) {
83 pim_upstream_update_join_desired(ch
->upstream
);
84 pim_ifchannel_update_could_assert(ch
);
85 pim_ifchannel_update_assert_tracking_desired(ch
);
89 static void on_trace(const char *label
,
90 struct interface
*ifp
, struct in_addr src
)
92 if (PIM_DEBUG_PIM_TRACE
) {
93 char src_str
[INET_ADDRSTRLEN
];
94 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
95 zlog_debug("%s: from %s on %s",
96 label
, src_str
, ifp
->name
);
100 static int preferred_assert(const struct pim_ifchannel
*ch
,
101 const struct pim_assert_metric
*recv_metric
)
103 return pim_assert_metric_better(recv_metric
,
104 &ch
->ifassert_winner_metric
);
107 static int acceptable_assert(const struct pim_assert_metric
*my_metric
,
108 const struct pim_assert_metric
*recv_metric
)
110 return pim_assert_metric_better(recv_metric
,
114 static int inferior_assert(const struct pim_assert_metric
*my_metric
,
115 const struct pim_assert_metric
*recv_metric
)
117 return pim_assert_metric_better(my_metric
,
121 static int cancel_assert(const struct pim_assert_metric
*recv_metric
)
123 return (recv_metric
->metric_preference
== PIM_ASSERT_METRIC_PREFERENCE_MAX
)
125 (recv_metric
->route_metric
== PIM_ASSERT_ROUTE_METRIC_MAX
);
128 static void if_could_assert_do_a1(const char *caller
,
129 struct pim_ifchannel
*ch
)
131 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
132 if (assert_action_a1(ch
)) {
133 zlog_warn("%s: %s: (S,G)=%s assert_action_a1 failure on interface %s",
134 __PRETTY_FUNCTION__
, caller
,
135 ch
->sg_str
, ch
->interface
->name
);
136 /* log warning only */
141 static int dispatch_assert(struct interface
*ifp
,
142 struct in_addr source_addr
,
143 struct in_addr group_addr
,
144 struct pim_assert_metric recv_metric
)
146 struct pim_ifchannel
*ch
;
149 memset (&sg
, 0, sizeof (struct prefix_sg
));
150 sg
.src
= source_addr
;
152 ch
= pim_ifchannel_add(ifp
, &sg
, 0);
154 zlog_warn("%s: (S,G)=%s failure creating channel on interface %s",
156 pim_str_sg_dump (&sg
), ifp
->name
);
160 switch (ch
->ifassert_state
) {
161 case PIM_IFASSERT_NOINFO
:
162 if (recv_metric
.rpt_bit_flag
) {
164 if_could_assert_do_a1(__PRETTY_FUNCTION__
, ch
);
168 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
169 if_could_assert_do_a1(__PRETTY_FUNCTION__
, ch
);
171 else if (acceptable_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
172 if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
)) {
173 assert_action_a6(ch
, recv_metric
);
178 case PIM_IFASSERT_I_AM_WINNER
:
179 if (preferred_assert(ch
, &recv_metric
)) {
180 assert_action_a2(ch
, recv_metric
);
183 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
184 assert_action_a3(ch
);
188 case PIM_IFASSERT_I_AM_LOSER
:
189 if (recv_metric
.ip_address
.s_addr
== ch
->ifassert_winner
.s_addr
) {
190 /* Assert from current winner */
192 if (cancel_assert(&recv_metric
)) {
193 assert_action_a5(ch
);
196 if (inferior_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
197 assert_action_a5(ch
);
199 else if (acceptable_assert(&ch
->ifassert_my_metric
, &recv_metric
)) {
200 if (!recv_metric
.rpt_bit_flag
) {
201 assert_action_a2(ch
, recv_metric
);
206 else if (preferred_assert(ch
, &recv_metric
)) {
207 assert_action_a2(ch
, recv_metric
);
212 zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
214 ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
222 int pim_assert_recv(struct interface
*ifp
,
223 struct pim_neighbor
*neigh
,
224 struct in_addr src_addr
,
225 uint8_t *buf
, int buf_size
)
228 struct prefix msg_source_addr
;
229 struct pim_assert_metric msg_metric
;
233 struct pim_interface
*pim_ifp
= NULL
;
235 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
238 curr_size
= buf_size
;
241 Parse assert group addr
243 memset (&sg
, 0, sizeof (struct prefix_sg
));
244 offset
= pim_parse_addr_group (&sg
, curr
, curr_size
);
246 char src_str
[INET_ADDRSTRLEN
];
247 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
248 zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
257 Parse assert source addr
259 offset
= pim_parse_addr_ucast (&msg_source_addr
, curr
, curr_size
);
261 char src_str
[INET_ADDRSTRLEN
];
262 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
263 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
271 if (curr_size
!= 8) {
272 char src_str
[INET_ADDRSTRLEN
];
273 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
274 zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
282 Parse assert metric preference
285 msg_metric
.metric_preference
= pim_read_uint32_host(curr
);
287 msg_metric
.rpt_bit_flag
= msg_metric
.metric_preference
& 0x80000000; /* save highest bit */
288 msg_metric
.metric_preference
&= ~0x80000000; /* clear highest bit */
293 Parse assert route metric
296 msg_metric
.route_metric
= pim_read_uint32_host(curr
);
298 if (PIM_DEBUG_PIM_TRACE
) {
299 char neigh_str
[INET_ADDRSTRLEN
];
300 char source_str
[INET_ADDRSTRLEN
];
301 char group_str
[INET_ADDRSTRLEN
];
302 pim_inet4_dump("<neigh?>", src_addr
, neigh_str
, sizeof(neigh_str
));
303 pim_inet4_dump("<src?>", msg_source_addr
.u
.prefix4
, source_str
, sizeof(source_str
));
304 pim_inet4_dump("<grp?>", sg
.grp
, group_str
, sizeof(group_str
));
305 zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
306 __PRETTY_FUNCTION__
, neigh_str
, ifp
->name
,
307 source_str
, group_str
,
308 msg_metric
.metric_preference
,
309 msg_metric
.route_metric
,
310 PIM_FORCE_BOOLEAN(msg_metric
.rpt_bit_flag
));
313 msg_metric
.ip_address
= src_addr
;
317 ++pim_ifp
->pim_ifstat_assert_recv
;
319 return dispatch_assert(ifp
,
320 msg_source_addr
.u
.prefix4
,
326 RFC 4601: 4.6.3. Assert Metrics
328 Assert metrics are defined as:
330 When comparing assert_metrics, the rpt_bit_flag, metric_preference,
331 and route_metric field are compared in order, where the first lower
332 value wins. If all fields are equal, the primary IP address of the
333 router that sourced the Assert message is used as a tie-breaker,
334 with the highest IP address winning.
336 int pim_assert_metric_better(const struct pim_assert_metric
*m1
,
337 const struct pim_assert_metric
*m2
)
339 if (m1
->rpt_bit_flag
< m2
->rpt_bit_flag
)
341 if (m1
->rpt_bit_flag
> m2
->rpt_bit_flag
)
344 if (m1
->metric_preference
< m2
->metric_preference
)
346 if (m1
->metric_preference
> m2
->metric_preference
)
349 if (m1
->route_metric
< m2
->route_metric
)
351 if (m1
->route_metric
> m2
->route_metric
)
354 return ntohl(m1
->ip_address
.s_addr
) > ntohl(m2
->ip_address
.s_addr
);
357 int pim_assert_metric_match(const struct pim_assert_metric
*m1
,
358 const struct pim_assert_metric
*m2
)
360 if (m1
->rpt_bit_flag
!= m2
->rpt_bit_flag
)
362 if (m1
->metric_preference
!= m2
->metric_preference
)
364 if (m1
->route_metric
!= m2
->route_metric
)
367 return m1
->ip_address
.s_addr
== m2
->ip_address
.s_addr
;
370 int pim_assert_build_msg(uint8_t *pim_msg
, int buf_size
,
371 struct interface
*ifp
,
372 struct in_addr group_addr
,
373 struct in_addr source_addr
,
374 uint32_t metric_preference
,
375 uint32_t route_metric
,
376 uint32_t rpt_bit_flag
)
378 uint8_t *buf_pastend
= pim_msg
+ buf_size
;
379 uint8_t *pim_msg_curr
;
383 pim_msg_curr
= pim_msg
+ PIM_MSG_HEADER_LEN
; /* skip room for pim header */
386 remain
= buf_pastend
- pim_msg_curr
;
387 pim_msg_curr
= pim_msg_addr_encode_ipv4_group(pim_msg_curr
, group_addr
);
389 char group_str
[INET_ADDRSTRLEN
];
390 pim_inet4_dump("<grp?>", group_addr
, group_str
, sizeof(group_str
));
391 zlog_warn("%s: failure encoding group address %s: space left=%d",
392 __PRETTY_FUNCTION__
, group_str
, remain
);
397 remain
= buf_pastend
- pim_msg_curr
;
398 pim_msg_curr
= pim_msg_addr_encode_ipv4_ucast(pim_msg_curr
, source_addr
);
400 char source_str
[INET_ADDRSTRLEN
];
401 pim_inet4_dump("<src?>", source_addr
, source_str
, sizeof(source_str
));
402 zlog_warn("%s: failure encoding source address %s: space left=%d",
403 __PRETTY_FUNCTION__
, source_str
, remain
);
407 /* Metric preference */
408 pim_write_uint32(pim_msg_curr
, rpt_bit_flag
?
409 metric_preference
| 0x80000000 :
414 pim_write_uint32(pim_msg_curr
, route_metric
);
420 pim_msg_size
= pim_msg_curr
- pim_msg
;
421 pim_msg_build_header(pim_msg
, pim_msg_size
,
422 PIM_MSG_TYPE_ASSERT
);
427 static int pim_assert_do(struct pim_ifchannel
*ch
,
428 struct pim_assert_metric metric
)
430 struct interface
*ifp
;
431 struct pim_interface
*pim_ifp
;
432 uint8_t pim_msg
[1000];
438 if (PIM_DEBUG_PIM_TRACE
)
439 zlog_debug("%s: channel%s has no associated interface!",
440 __PRETTY_FUNCTION__
, ch
->sg_str
);
445 if (PIM_DEBUG_PIM_TRACE
)
446 zlog_debug("%s: channel %s pim not enabled on interface: %s",
447 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
451 pim_msg_size
= pim_assert_build_msg(pim_msg
, sizeof(pim_msg
), ifp
,
452 ch
->sg
.grp
, ch
->sg
.src
,
453 metric
.metric_preference
,
455 metric
.rpt_bit_flag
);
456 if (pim_msg_size
< 1) {
457 zlog_warn("%s: failure building PIM assert message: msg_size=%d",
458 __PRETTY_FUNCTION__
, pim_msg_size
);
463 RFC 4601: 4.3.1. Sending Hello Messages
465 Thus, if a router needs to send a Join/Prune or Assert message on
466 an interface on which it has not yet sent a Hello message with the
467 currently configured IP address, then it MUST immediately send the
468 relevant Hello message without waiting for the Hello Timer to
469 expire, followed by the Join/Prune or Assert message.
471 pim_hello_require(ifp
);
473 if (PIM_DEBUG_PIM_TRACE
) {
474 zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u",
476 ifp
->name
, ch
->sg_str
,
477 metric
.metric_preference
,
479 PIM_FORCE_BOOLEAN(metric
.rpt_bit_flag
));
481 ++pim_ifp
->pim_ifstat_assert_send
;
483 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
484 pim_ifp
->primary_address
,
485 qpim_all_pim_routers_addr
,
489 zlog_warn("%s: could not send PIM message on interface %s",
490 __PRETTY_FUNCTION__
, ifp
->name
);
497 int pim_assert_send(struct pim_ifchannel
*ch
)
499 return pim_assert_do(ch
, ch
->ifassert_my_metric
);
503 RFC 4601: 4.6.4. AssertCancel Messages
505 An AssertCancel(S,G) is an infinite metric assert with the RPT bit
506 set that names S as the source.
508 static int pim_assert_cancel(struct pim_ifchannel
*ch
)
510 struct pim_assert_metric metric
;
512 metric
.rpt_bit_flag
= 0;
513 metric
.metric_preference
= PIM_ASSERT_METRIC_PREFERENCE_MAX
;
514 metric
.route_metric
= PIM_ASSERT_ROUTE_METRIC_MAX
;
515 metric
.ip_address
= ch
->sg
.src
;
517 return pim_assert_do(ch
, metric
);
520 static int on_assert_timer(struct thread
*t
)
522 struct pim_ifchannel
*ch
;
523 struct interface
*ifp
;
529 if (PIM_DEBUG_PIM_TRACE
) {
530 zlog_debug("%s: (S,G)=%s timer expired on interface %s",
532 ch
->sg_str
, ifp
->name
);
535 ch
->t_ifassert_timer
= NULL
;
537 switch (ch
->ifassert_state
) {
538 case PIM_IFASSERT_I_AM_WINNER
:
539 assert_action_a3(ch
);
541 case PIM_IFASSERT_I_AM_LOSER
:
542 assert_action_a5(ch
);
546 if (PIM_DEBUG_PIM_EVENTS
)
547 zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s",
549 ch
->sg_str
, ch
->ifassert_state
, ifp
->name
);
556 static void assert_timer_off(struct pim_ifchannel
*ch
)
558 if (PIM_DEBUG_PIM_TRACE
) {
559 if (ch
->t_ifassert_timer
) {
560 zlog_debug("%s: (S,G)=%s cancelling timer on interface %s",
562 ch
->sg_str
, ch
->interface
->name
);
565 THREAD_OFF(ch
->t_ifassert_timer
);
568 static void pim_assert_timer_set(struct pim_ifchannel
*ch
,
571 assert_timer_off(ch
);
573 if (PIM_DEBUG_PIM_TRACE
) {
574 zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s",
576 ch
->sg_str
, interval
, ch
->interface
->name
);
579 thread_add_timer(master
, on_assert_timer
, ch
, interval
,
580 &ch
->t_ifassert_timer
);
583 static void pim_assert_timer_reset(struct pim_ifchannel
*ch
)
585 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
- PIM_ASSERT_OVERRIDE_INTERVAL
);
589 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
591 (S,G) Assert State machine Actions
593 A1: Send Assert(S,G).
594 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
595 Store self as AssertWinner(S,G,I).
596 Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
598 int assert_action_a1(struct pim_ifchannel
*ch
)
600 struct interface
*ifp
= ch
->interface
;
601 struct pim_interface
*pim_ifp
;
605 zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s",
607 ch
->sg_str
, ifp
->name
);
608 return -1; /* must return since pim_ifp is used below */
611 /* Switch to I_AM_WINNER before performing action_a3 below */
612 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_WINNER
,
613 pim_ifp
->primary_address
,
614 pim_macro_spt_assert_metric(&ch
->upstream
->rpf
,
615 pim_ifp
->primary_address
));
617 if (assert_action_a3(ch
)) {
618 zlog_warn("%s: (S,G)=%s assert_action_a3 failure on interface %s",
620 ch
->sg_str
, ifp
->name
);
624 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
)
626 if (PIM_DEBUG_PIM_EVENTS
)
627 zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state",
628 __PRETTY_FUNCTION__
, ch
->sg_str
);
635 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
637 (S,G) Assert State machine Actions
639 A2: Store new assert winner as AssertWinner(S,G,I) and assert
640 winner metric as AssertWinnerMetric(S,G,I).
641 Set Assert Timer to Assert_Time.
643 static void assert_action_a2(struct pim_ifchannel
*ch
,
644 struct pim_assert_metric winner_metric
)
646 pim_ifassert_winner_set(ch
, PIM_IFASSERT_I_AM_LOSER
,
647 winner_metric
.ip_address
,
650 pim_assert_timer_set(ch
, PIM_ASSERT_TIME
);
652 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
)
654 if (PIM_DEBUG_PIM_EVENTS
)
655 zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state",
656 __PRETTY_FUNCTION__
, ch
->sg_str
);
661 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
663 (S,G) Assert State machine Actions
665 A3: Send Assert(S,G).
666 Set Assert Timer to (Assert_Time - Assert_Override_Interval).
668 static int assert_action_a3(struct pim_ifchannel
*ch
)
670 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_WINNER
)
672 if (PIM_DEBUG_PIM_EVENTS
)
673 zlog_warn("%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state",
674 __PRETTY_FUNCTION__
, ch
->sg_str
);
678 pim_assert_timer_reset(ch
);
680 if (pim_assert_send(ch
)) {
681 zlog_warn("%s: (S,G)=%s failure sending assert on interface %s",
683 ch
->sg_str
, ch
->interface
->name
);
691 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
693 (S,G) Assert State machine Actions
695 A4: Send AssertCancel(S,G).
696 Delete assert info (AssertWinner(S,G,I) and
697 AssertWinnerMetric(S,G,I) will then return their default
700 void assert_action_a4(struct pim_ifchannel
*ch
)
702 if (pim_assert_cancel(ch
)) {
703 zlog_warn("%s: failure sending AssertCancel%s on interface %s",
705 ch
->sg_str
, ch
->interface
->name
);
706 /* log warning only */
709 assert_action_a5(ch
);
711 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
)
713 if (PIM_DEBUG_PIM_EVENTS
)
714 zlog_warn("%s: channel%s not in PIM_IFASSERT_NOINFO state as expected",
715 __PRETTY_FUNCTION__
, ch
->sg_str
);
720 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
722 (S,G) Assert State machine Actions
724 A5: Delete assert info (AssertWinner(S,G,I) and
725 AssertWinnerMetric(S,G,I) will then return their default values).
727 void assert_action_a5(struct pim_ifchannel
*ch
)
729 reset_ifassert_state(ch
);
730 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
)
732 if (PIM_DEBUG_PIM_EVENTS
)
733 zlog_warn("%s: channel%s not in PIM_IFSSERT_NOINFO state as expected",
734 __PRETTY_FUNCTION__
, ch
->sg_str
);
739 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
741 (S,G) Assert State machine Actions
743 A6: Store new assert winner as AssertWinner(S,G,I) and assert
744 winner metric as AssertWinnerMetric(S,G,I).
745 Set Assert Timer to Assert_Time.
746 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
747 set SPTbit(S,G) to TRUE.
749 static void assert_action_a6(struct pim_ifchannel
*ch
,
750 struct pim_assert_metric winner_metric
)
752 assert_action_a2(ch
, winner_metric
);
755 If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
758 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ch
->interface
)
759 if (ch
->upstream
->join_state
== PIM_UPSTREAM_JOINED
)
760 ch
->upstream
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
762 if (ch
->ifassert_state
!= PIM_IFASSERT_I_AM_LOSER
)
764 if(PIM_DEBUG_PIM_EVENTS
)
765 zlog_warn("%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected",
766 __PRETTY_FUNCTION__
, ch
->sg_str
);