#include #include #include #include #include #include #include #include // for inet_pton #include #include // for if_indextoname // bool型 typedef _Bool bool; #define false 0 #define true 1 /** * @breif 指定されたインタフェースのIPv6アドレスを取得する * @param[in] ifindex インタフェースインデックスの指定 * @param[out] in6 インタフェースについているアドレス * @param[in] グローバルならtrue, リンクローカルならfalse * @retval 0 成功 * @retval 非0 失敗 */ static int if_get_ipv6addr(const char *ifname, struct in6_addr *in6, bool is_global){ struct ifaddrs *ifa_list, *ifa; bool found = false; memset(in6, 0 ,sizeof(struct in6_addr)); if(getifaddrs(&ifa_list) != 0){ perror("getifaddrs"); return -1; } for(ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next){ if(strcmp(ifname, ifa->ifa_name)) continue; if(ifa->ifa_addr->sa_family == AF_INET6){ struct in6_addr *tmp = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; // リンクローカルとグローバル以外のスコープのアドレスが // インタフェースに付けていない前提ですが、サイトローカル // などは廃止されているので大丈夫でしょう if(!is_global ^ IN6_IS_ADDR_LINKLOCAL(tmp)) continue; memcpy(in6, tmp, sizeof(struct in6_addr)); found = true; } } freeifaddrs(ifa_list); return found ? 0 : -2; } /** * @breif 指定されたインタフェースのリンクローカルIPv6アドレスを取得する * @param[in] ifindex インタフェースインデックスの指定 * @param[out] in6 インタフェースについているアドレス * @retval 0 成功 * @retval 非0 失敗 */ inline int if_get_local_ipv6addr(const char *ifname, struct in6_addr *in6){ return if_get_ipv6addr(ifname, in6, false); } /** * @breif 指定されたインタフェースのグローバルIPv6アドレスを取得する * @param[in] ifindex インタフェースインデックスの指定 * @param[out] in6 インタフェースについているアドレス * @retval 0 成功 * @retval 非0 失敗 */ inline int if_get_global_ipv6addr(const char *ifname, struct in6_addr *in6){ return if_get_ipv6addr(ifname, in6, true); } /** * @brief メッセージを送信する * @param[in] dstaddr 送信先アドレス * @param[in] dstport 送信先ポート * @param[in] msg 送信するメッセージ * @param[in] msglen メッセージ長 * @param[in] ifindex 送信するインタフェースインデックス */ static int send_with_ancillary_pktinfo(const struct in6_addr *dstaddr, int dstport, const void *msg, int msglen, int ifindex){ int soc, err; char ifname[IFNAMSIZ]; struct msghdr msghdr; struct iovec iov; struct cmsghdr *cmsg; int cmsglen; struct in6_pktinfo *pinfo; struct sockaddr_in6 dst; struct in6_addr srcaddr; char dstaddrstr[INET6_ADDRSTRLEN], srcaddrstr[INET6_ADDRSTRLEN]; // ソケットを開く if((soc = socket(AF_INET6, SOCK_DGRAM, 0)) < 0){ perror("socket"); return -1; } // 送信元アドレスをifindexから決定する if(if_get_global_ipv6addr(if_indextoname(ifindex, ifname), &srcaddr) != 0){ perror("if_get_global_ipv6addr"); goto fail; } printf("Sending IPv6 packet by using %s.\n", __FUNCTION__); printf("Source : %s\n", inet_ntop(AF_INET6, &srcaddr, srcaddrstr, sizeof(srcaddrstr))); printf("Destination: %s\n", inet_ntop(AF_INET6, dstaddr, dstaddrstr, sizeof(dstaddrstr))); printf("Interface : %s (%d)\n", if_indextoname(ifindex, ifname), ifindex); // 制御メッセージ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cmsg = (struct cmsghdr *)malloc(cmsglen); if(cmsg == NULL){ perror("malloc"); goto fail; } // 宛先 memset(&dst, 0, sizeof(struct sockaddr_in6)); dst.sin6_family = AF_INET6; memcpy(&dst.sin6_addr, dstaddr, sizeof(struct in6_addr)); dst.sin6_port = htons(dstport); dst.sin6_scope_id = ifindex; // アドレスがリンクローカルスコープの場合のみ必要? // メッセージヘッダ memset(&msghdr, 0, sizeof(struct msghdr)); msghdr.msg_control = cmsg; msghdr.msg_controllen = cmsglen; msghdr.msg_iov = &iov; iov.iov_base = (void *)msg; iov.iov_len = msglen; msghdr.msg_iovlen = 1; msghdr.msg_name = &dst; msghdr.msg_namelen = sizeof(dst); // ヘッダ memset(cmsg, 0, cmsglen); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); // in6_pktinfo構造体へのデータセット memcpy(&pinfo->ipi6_addr, &srcaddr, sizeof(struct in6_addr)); pinfo->ipi6_ifindex = ifindex; if((err = sendmsg(soc, &msghdr, 0)) < 0){ perror("sendmsg"); goto out; } goto out; fail: err = -1; out: free(cmsg); close(soc); return err; } /** * @brief メッセージを送信する * @param[in] dstaddr 送信先アドレス * @param[in] dstport 送信先ポート * @param[in] msg 送信するメッセージ * @param[in] msglen メッセージ長 * @param[in] ifindex 送信するインタフェースインデックス */ static int send_with_sticky_pktinfo(const struct in6_addr *dstaddr, int dstport, const void *msg, int msglen, int ifindex){ int soc, err; char ifname[IFNAMSIZ]; struct in6_pktinfo pinfo; struct sockaddr_in6 dst; struct in6_addr srcaddr; char dstaddrstr[INET6_ADDRSTRLEN], srcaddrstr[INET6_ADDRSTRLEN]; // ソケットを開く if((soc = socket(AF_INET6, SOCK_DGRAM, 0)) < 0){ perror("socket"); return -1; } // 送信元アドレスをifindexから決定する if(if_get_global_ipv6addr(if_indextoname(ifindex, ifname), &srcaddr) != 0){ perror("if_get_global_ipv6addr"); goto fail; } printf("Sending IPv6 packet by using %s.\n", __FUNCTION__); printf("Source : %s\n", inet_ntop(AF_INET6, &srcaddr, srcaddrstr, sizeof(srcaddrstr))); printf("Destination: %s\n", inet_ntop(AF_INET6, dstaddr, dstaddrstr, sizeof(dstaddrstr))); printf("Interface : %s (%d)\n", if_indextoname(ifindex, ifname), ifindex); // 宛先 memset(&dst, 0, sizeof(struct sockaddr_in6)); dst.sin6_family = AF_INET6; memcpy(&dst.sin6_addr, dstaddr, sizeof(struct in6_addr)); dst.sin6_port = htons(dstport); dst.sin6_scope_id = ifindex; // アドレスがリンクローカルスコープの場合のみ必要? // in6_pktinfo構造体へのデータセット memcpy(&pinfo.ipi6_addr, &srcaddr, sizeof(struct in6_addr)); pinfo.ipi6_ifindex = ifindex; // IPV6_PKTINFOを設定 if(setsockopt(soc, IPPROTO_IPV6, IPV6_PKTINFO, &pinfo, sizeof(struct in6_pktinfo)) == -1){ perror("setsockopt"); goto fail; } if((err = sendto(soc, msg, msglen, 0, (struct sockaddr *)&dst, sizeof(dst))) < 0){ perror("sendto"); goto out; } goto out; fail: err = -1; out: close(soc); return err; } /** * @brief メッセージを送信する * @param[in] dstaddr 送信先アドレス * @param[in] dstport 送信先ポート * @param[in] msg 送信するメッセージ * @param[in] msglen メッセージ長 * @param[in] ifindex 送信するインタフェースインデックス */ static int send_with_2292pktinfo(const struct in6_addr *dstaddr, int dstport, const void *msg, int msglen, int ifindex){ int soc, err; char ifname[IFNAMSIZ]; struct msghdr msghdr; struct iovec iov; struct cmsghdr *cmsg; int cmsglen; struct in6_pktinfo *pinfo; const int on = 1; struct sockaddr_in6 dst; struct in6_addr srcaddr; char dstaddrstr[INET6_ADDRSTRLEN], srcaddrstr[INET6_ADDRSTRLEN]; // ソケットを開く if((soc = socket(AF_INET6, SOCK_DGRAM, 0)) < 0){ perror("socket"); return -1; } // 送信元アドレスをifindexから決定する if(if_get_global_ipv6addr(if_indextoname(ifindex, ifname), &srcaddr) != 0){ perror("if_get_global_ipv6addr"); goto fail; } printf("Sending IPv6 packet by using %s.\n", __FUNCTION__); printf("Source : %s\n", inet_ntop(AF_INET6, &srcaddr, srcaddrstr, sizeof(srcaddrstr))); printf("Destination: %s\n", inet_ntop(AF_INET6, dstaddr, dstaddrstr, sizeof(dstaddrstr))); printf("Interface : %s (%d)\n", if_indextoname(ifindex, ifname), ifindex); // 制御メッセージ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cmsg = (struct cmsghdr *)malloc(cmsglen); if(cmsg == NULL){ perror("malloc"); goto fail; } // 宛先 memset(&dst, 0, sizeof(struct sockaddr_in6)); dst.sin6_family = AF_INET6; memcpy(&dst.sin6_addr, dstaddr, sizeof(struct in6_addr)); dst.sin6_port = htons(dstport); dst.sin6_scope_id = ifindex; // アドレスがリンクローカルスコープの場合のみ必要? // メッセージヘッダ memset(&msghdr, 0, sizeof(struct msghdr)); msghdr.msg_control = cmsg; msghdr.msg_controllen = cmsglen; msghdr.msg_iov = &iov; iov.iov_base = (void *)msg; iov.iov_len = msglen; msghdr.msg_iovlen = 1; msghdr.msg_name = &dst; msghdr.msg_namelen = sizeof(dst); // ヘッダ memset(cmsg, 0, cmsglen); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_2292PKTINFO; pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); // in6_pktinfo構造体へのデータセット memcpy(&pinfo->ipi6_addr, &srcaddr, sizeof(struct in6_addr)); pinfo->ipi6_ifindex = ifindex; // IPV6_2292PKTINFOを設定 if(setsockopt(soc, IPPROTO_IPV6, IPV6_2292PKTINFO, &on, sizeof(int)) == -1){ perror("setsockopt"); goto fail; } if((err = sendmsg(soc, &msghdr, 0)) < 0){ perror("sendmsg"); goto out; } goto out; fail: err = -1; out: free(cmsg); close(soc); return err; } int main(int argc, char **argv){ struct in6_addr dstaddr; char buf[64], ifname[IFNAMSIZ]; int i; memset(&dstaddr, 0, sizeof(dstaddr)); // 宛先アドレスの設定 inet_pton(AF_INET6, "2001:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx", &dstaddr); // 送信に使用するインタフェース名を設定 strcpy(ifname, "eth0"); memset(buf, 0, sizeof(buf)); // 適当なペイロードを作る for(i=0; i